Merge "Merge 24Q4 (ab/12406339) into aosp-main-future" into aosp-main-future

This commit is contained in:
Xin Li
2024-11-13 21:43:11 +00:00
committed by Android (Google) Code Review
219 changed files with 6009 additions and 1705 deletions

View File

@@ -55,6 +55,9 @@ union HapticGenerator {
parcelable HapticScale {
int id;
android.hardware.audio.effect.HapticGenerator.VibratorScale scale = android.hardware.audio.effect.HapticGenerator.VibratorScale.MUTE;
float scaleFactor = (-1.0f) /* -1.000000f */;
float adaptiveScaleFactor = (-1.0f) /* -1.000000f */;
const float UNDEFINED_SCALE_FACTOR = (-1.0f) /* -1.000000f */;
}
@VintfStability
parcelable VibratorInformation {

View File

@@ -55,14 +55,52 @@ union HapticGenerator {
@VintfStability
parcelable HapticScale {
/**
* Representation of undefined scale factor, applied by default for backwards compatibility.
*/
const float UNDEFINED_SCALE_FACTOR = -1.0f;
/**
* Audio track ID.
*/
int id;
/**
* Haptic intensity.
*
* This represents haptics scale as fixed levels defined by VibrationScale. If the field
* scaleFactor is defined then this will be ignored in favor of scaleFactor, otherwise this
* will be used to define the intensity for the haptics.
*/
VibratorScale scale = VibratorScale.MUTE;
/**
* Haptic scale factor.
*
* This is a continuous scale representation of VibratorScale, allowing flexible number of
* scale levels. If this field is defined then it will be used to define the intensity of
* the haptics, instead of the old VibratorScale field. If this field is undefined then the
* old VibratorScale field will be used.
*
* The value zero represents the same as VibratorScale.MUTE and the value one represents
* VibratorScale.NONE. Values in (0,1) should scale down, and values > 1 should scale up
* within hardware bounds. Negative values will be ignored.
*/
float scaleFactor = -1.0f; // UNDEFINED_SCALE_FACTOR
/**
* Haptic adaptive scale factor.
*
* This is an additional scale value that should be applied on top of the vibrator scale to
* adapt to the device current state. This should be applied to linearly scale the haptic
* data after scale/scaleFactor is applied.
*
* The value zero mutes the haptics, even if the scale/scaleFactor are not set to MUTE/zero.
* The value one will not scale the haptics, and can be used as a constant for no-op.
* Values in (0,1) should scale down. Values > 1 should scale up within hardware bounds.
* Negative values will be ignored.
*/
float adaptiveScaleFactor = -1.0f; // UNDEFINED_SCALE_FACTOR
}
/**

View File

@@ -136,6 +136,9 @@ cc_test {
name: "VtsHalHapticGeneratorTargetTest",
defaults: ["VtsHalAudioEffectTargetTestDefaults"],
srcs: ["VtsHalHapticGeneratorTargetTest.cpp"],
shared_libs: [
"libaudioutils",
],
}
cc_test {

View File

@@ -479,6 +479,17 @@ class EffectHelper {
mOutputSamples = common.output.frameCount * mOutputFrameSize / sizeof(float);
}
void generateInput(std::vector<float>& input, float inputFrequency, float samplingFrequency,
size_t inputSize = 0) {
if (inputSize == 0 || inputSize > input.size()) {
inputSize = input.size();
}
for (size_t i = 0; i < inputSize; i++) {
input[i] = sin(2 * M_PI * inputFrequency * i / samplingFrequency);
}
}
bool mIsSpatializer;
Descriptor mDescriptor;
size_t mInputFrameSize, mOutputFrameSize;

View File

@@ -21,11 +21,13 @@
#define LOG_TAG "VtsHalHapticGeneratorTargetTest"
#include <android-base/logging.h>
#include <android/binder_enums.h>
#include <audio_utils/power.h>
#include "EffectHelper.h"
using namespace android;
using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::getEffectTypeUuidHapticGenerator;
using aidl::android::hardware::audio::effect::HapticGenerator;
@@ -34,406 +36,412 @@ using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using android::hardware::audio::common::testing::detail::TestExecutionTracer;
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
* VtsAudioEffectTargetTest.
*/
enum ParamName {
PARAM_INSTANCE_NAME,
PARAM_HAPTIC_SCALE_ID,
PARAM_HAPTIC_SCALE_VIBRATOR_SCALE,
PARAM_VIBRATION_INFORMATION_RESONANT_FREQUENCY,
PARAM_VIBRATION_INFORMATION_Q_FACTOR,
PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE,
};
using HapticGeneratorParamTestParam =
std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int,
HapticGenerator::VibratorScale, float, float, float>;
/*
* Testing parameter range, assuming the parameter supported by effect is in this range.
* Parameter should be within the valid range defined in the documentation,
* for any supported value test expects EX_NONE from IEffect.setParameter(),
* otherwise expect EX_ILLEGAL_ARGUMENT.
*/
// TODO : Update the test values once range/capability is updated by implementation
const int MIN_ID = std::numeric_limits<int>::min();
const int MAX_ID = std::numeric_limits<int>::max();
const float MIN_FLOAT = std::numeric_limits<float>::min();
const float MAX_FLOAT = std::numeric_limits<float>::max();
const std::vector<int> kHapticScaleIdValues = {MIN_ID, 0, MAX_ID};
const std::vector<HapticGenerator::VibratorScale> kVibratorScaleValues = {
std::vector<HapticGenerator::VibratorScale> kScaleValues = {
ndk::enum_range<HapticGenerator::VibratorScale>().begin(),
ndk::enum_range<HapticGenerator::VibratorScale>().end()};
const std::vector<float> kScaleFactorValues = {HapticGenerator::HapticScale::UNDEFINED_SCALE_FACTOR,
0.0f, 0.5f, 1.0f, MAX_FLOAT};
const std::vector<float> kAdaptiveScaleFactorValues = {
HapticGenerator::HapticScale::UNDEFINED_SCALE_FACTOR, 0.0f, 0.5f, 1.0f, MAX_FLOAT};
const std::vector<float> kResonantFrequencyValues = {MIN_FLOAT, 100, MAX_FLOAT};
const std::vector<float> kQFactorValues = {MIN_FLOAT, 100, MAX_FLOAT};
const std::vector<float> kMaxAmplitude = {MIN_FLOAT, 100, MAX_FLOAT};
class HapticGeneratorParamTest : public ::testing::TestWithParam<HapticGeneratorParamTestParam>,
public EffectHelper {
constexpr int HAPTIC_SCALE_FACTORS_EFFECT_MIN_VERSION = 3;
static const std::vector<int32_t> kHapticOutputLayouts = {
AudioChannelLayout::LAYOUT_MONO_HAPTIC_A, AudioChannelLayout::LAYOUT_MONO_HAPTIC_AB,
AudioChannelLayout::LAYOUT_STEREO_HAPTIC_A, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_AB};
class HapticGeneratorHelper : public EffectHelper {
public:
HapticGeneratorParamTest()
: mParamHapticScaleId(std::get<PARAM_HAPTIC_SCALE_ID>(GetParam())),
mParamVibratorScale(std::get<PARAM_HAPTIC_SCALE_VIBRATOR_SCALE>(GetParam())),
mParamResonantFrequency(
std::get<PARAM_VIBRATION_INFORMATION_RESONANT_FREQUENCY>(GetParam())),
mParamQFactor(std::get<PARAM_VIBRATION_INFORMATION_Q_FACTOR>(GetParam())),
mParamMaxAmplitude(std::get<PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE>(GetParam())) {
std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
}
void SetUp() override {
void SetUpHapticGenerator(int32_t chMask = AudioChannelLayout::CHANNEL_HAPTIC_A) {
ASSERT_NE(nullptr, mFactory);
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
EXPECT_STATUS(EX_NONE, mEffect->getInterfaceVersion(&mEffectInterfaceVersion));
AudioChannelLayout layout =
AudioChannelLayout::make<AudioChannelLayout::layoutMask>(chMask);
Parameter::Common common = createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
IEffect::OpenEffectReturn ret;
0 /* session */, 1 /* ioHandle */, kSamplingFrequency /* iSampleRate */,
kSamplingFrequency /* oSampleRate */, kFrameCount /* iFrameCount */,
kFrameCount /* oFrameCount */, layout, layout);
ASSERT_NO_FATAL_FAILURE(open(mEffect, common, std::nullopt, &ret, EX_NONE));
ASSERT_NE(nullptr, mEffect);
}
void TearDown() override {
void TearDownHapticGenerator() {
ASSERT_NO_FATAL_FAILURE(close(mEffect));
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
ret = IEffect::OpenEffectReturn{};
}
static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
Parameter createScaleParam(const std::vector<HapticGenerator::HapticScale>& hapticScales) {
return Parameter::make<Parameter::specific>(
Parameter::Specific::make<Parameter::Specific::hapticGenerator>(
HapticGenerator::make<HapticGenerator::hapticScales>(hapticScales)));
}
Parameter createVibratorParam(HapticGenerator::VibratorInformation vibrationInfo) {
return Parameter::make<Parameter::specific>(
Parameter::Specific::make<Parameter::Specific::hapticGenerator>(
HapticGenerator::make<HapticGenerator::vibratorInfo>(vibrationInfo)));
}
void setAndVerifyParameter(Parameter hapticParameter, HapticGenerator::Tag tag,
binder_exception_t expected = EX_NONE) {
EXPECT_STATUS(expected, mEffect->setParameter(hapticParameter))
<< hapticParameter.toString();
if (expected == EX_NONE) {
// get parameter
Parameter getParam;
auto second = Parameter::Id::make<Parameter::Id::hapticGeneratorTag>(
HapticGenerator::Id::make<HapticGenerator::Id::commonTag>(
HapticGenerator::Tag(tag)));
// If the set is successful, get param should match
EXPECT_STATUS(expected, mEffect->getParameter(second, &getParam));
EXPECT_EQ(hapticParameter, getParam) << "\nexpectedParam:" << hapticParameter.toString()
<< "\ngetParam:" << getParam.toString();
}
}
HapticGenerator::VibratorInformation createVibratorInfo(float resonantFrequency, float qFactor,
float amplitude) {
return HapticGenerator::VibratorInformation(resonantFrequency, qFactor, amplitude);
}
static const long kFrameCount = 10000;
static constexpr int kSamplingFrequency = 44100;
static constexpr int kDefaultScaleID = 0;
static constexpr float kDefaultMaxAmp = 1;
static constexpr float kDefaultResonantFrequency = 150;
static constexpr float kDefaultQfactor = 8;
static constexpr HapticGenerator::VibratorScale kDefaultScale =
HapticGenerator::VibratorScale::NONE;
std::shared_ptr<IFactory> mFactory;
std::shared_ptr<IEffect> mEffect;
Descriptor mDescriptor;
int mParamHapticScaleId = 0;
HapticGenerator::VibratorScale mParamVibratorScale = HapticGenerator::VibratorScale::MUTE;
float mParamResonantFrequency = 0;
float mParamQFactor = 0;
float mParamMaxAmplitude = 0;
IEffect::OpenEffectReturn ret;
Parameter mHapticSpecificParameter;
Parameter::Id mHapticIdParameter;
int mEffectInterfaceVersion;
};
void SetAndGetHapticGeneratorParameters() {
for (auto& it : mTags) {
auto& tag = std::get<ParamTestEnum::PARAM_TEST_TAG>(it);
auto& setHg = std::get<ParamTestEnum::PARAM_TEST_TARGET>(it);
/**
*Tests do the following:
* -Testing parameter range supported by the effect.
* -For any supported value test expects EX_NONE from IEffect.setParameter(),
* otherwise expect EX_ILLEGAL_ARGUMENT.
* -Validating the effect by comparing the output energies of the supported parameters.
**/
// set parameter
Parameter expectParam;
Parameter::Specific specific;
specific.set<Parameter::Specific::hapticGenerator>(setHg);
expectParam.set<Parameter::specific>(specific);
using EffectInstance = std::pair<std::shared_ptr<IFactory>, Descriptor>;
const bool valid =
isParameterValid<HapticGenerator, Range::hapticGenerator>(setHg, mDescriptor);
const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
EXPECT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString();
class HapticGeneratorScaleParamTest : public ::testing::TestWithParam<EffectInstance>,
public HapticGeneratorHelper {
public:
HapticGeneratorScaleParamTest() { std::tie(mFactory, mDescriptor) = GetParam(); }
void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpHapticGenerator()); }
void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownHapticGenerator()); }
};
// only get if parameter in range and set success
if (expected == EX_NONE) {
Parameter getParam;
Parameter::Id id;
HapticGenerator::Id hgId;
hgId.set<HapticGenerator::Id::commonTag>(tag);
id.set<Parameter::Id::hapticGeneratorTag>(hgId);
EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
EXPECT_EQ(expectParam, getParam) << expectParam.toString() << "\n"
<< getParam.toString();
TEST_P(HapticGeneratorScaleParamTest, SetAndGetScales) {
std::vector<HapticGenerator::HapticScale> hapticScales;
for (int i = 0; i < static_cast<int>(kScaleValues.size()); i++) {
hapticScales.push_back({.id = i, .scale = kScaleValues[i]});
}
ASSERT_NO_FATAL_FAILURE(
setAndVerifyParameter(createScaleParam(hapticScales), HapticGenerator::hapticScales));
}
TEST_P(HapticGeneratorScaleParamTest, SetAndGetScaleFactors) {
if (mEffectInterfaceVersion < HAPTIC_SCALE_FACTORS_EFFECT_MIN_VERSION) {
GTEST_SKIP() << "Skipping HapticGenerator ScaleFactors test for effect version "
<< std::to_string(mEffectInterfaceVersion);
}
std::vector<HapticGenerator::HapticScale> hapticScales;
for (int i = 0; i < static_cast<int>(kScaleFactorValues.size()); i++) {
hapticScales.push_back(
{.id = i, .scale = kScaleValues[0], .scaleFactor = kScaleFactorValues[i]});
}
ASSERT_NO_FATAL_FAILURE(
setAndVerifyParameter(createScaleParam(hapticScales), HapticGenerator::hapticScales));
}
TEST_P(HapticGeneratorScaleParamTest, SetAndGetAdaptiveScaleFactors) {
if (mEffectInterfaceVersion < HAPTIC_SCALE_FACTORS_EFFECT_MIN_VERSION) {
GTEST_SKIP() << "Skipping HapticGenerator AdaptiveScaleFactors test for effect version "
<< std::to_string(mEffectInterfaceVersion);
}
std::vector<HapticGenerator::HapticScale> hapticScales;
for (int i = 0; i < static_cast<int>(kAdaptiveScaleFactorValues.size()); i++) {
hapticScales.push_back({.id = i,
.scale = kScaleValues[0],
.scaleFactor = kScaleFactorValues[3],
.adaptiveScaleFactor = kAdaptiveScaleFactorValues[i]});
}
ASSERT_NO_FATAL_FAILURE(
setAndVerifyParameter(createScaleParam(hapticScales), HapticGenerator::hapticScales));
}
INSTANTIATE_TEST_SUITE_P(
HapticGeneratorValidTest, HapticGeneratorScaleParamTest,
testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
IFactory::descriptor, getEffectTypeUuidHapticGenerator())),
[](const testing::TestParamInfo<HapticGeneratorScaleParamTest::ParamType>& info) {
auto descriptor = info.param;
return getPrefix(descriptor.second);
});
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorScaleParamTest);
enum VibratorParamName {
VIBRATOR_PARAM_INSTANCE,
VIBRATOR_PARAM_RESONANT_FREQUENCY,
VIBRATOR_PARAM_Q_FACTOR,
VIBRATOR_PARAM_MAX_AMPLITUDE,
};
using HapticGeneratorVibratorInfoTestParam = std::tuple<EffectInstance, float, float, float>;
class HapticGeneratorVibratorInfoParamTest
: public ::testing::TestWithParam<HapticGeneratorVibratorInfoTestParam>,
public HapticGeneratorHelper {
public:
HapticGeneratorVibratorInfoParamTest()
: mParamResonantFrequency(std::get<VIBRATOR_PARAM_RESONANT_FREQUENCY>(GetParam())),
mParamQFactor(std::get<VIBRATOR_PARAM_Q_FACTOR>(GetParam())),
mParamMaxAmplitude(std::get<VIBRATOR_PARAM_MAX_AMPLITUDE>(GetParam())) {
std::tie(mFactory, mDescriptor) = std::get<VIBRATOR_PARAM_INSTANCE>(GetParam());
}
void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpHapticGenerator()); }
void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownHapticGenerator()); }
float mParamResonantFrequency = kDefaultResonantFrequency;
float mParamQFactor = kDefaultQfactor;
float mParamMaxAmplitude = kDefaultMaxAmp;
};
TEST_P(HapticGeneratorVibratorInfoParamTest, SetAndGetVibratorInformation) {
auto vibratorInfo =
createVibratorInfo(mParamResonantFrequency, mParamQFactor, mParamMaxAmplitude);
if (isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(createVibratorParam(vibratorInfo),
HapticGenerator::vibratorInfo));
} else {
ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(createVibratorParam(vibratorInfo),
HapticGenerator::vibratorInfo,
EX_ILLEGAL_ARGUMENT));
}
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorVibratorInfoParamTest);
INSTANTIATE_TEST_SUITE_P(
HapticGeneratorValidTest, HapticGeneratorVibratorInfoParamTest,
::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
IFactory::descriptor, getEffectTypeUuidHapticGenerator())),
testing::ValuesIn(kResonantFrequencyValues),
testing::ValuesIn(kQFactorValues), testing::ValuesIn(kMaxAmplitude)),
[](const testing::TestParamInfo<HapticGeneratorVibratorInfoParamTest::ParamType>& info) {
auto descriptor = std::get<VIBRATOR_PARAM_INSTANCE>(info.param).second;
std::string resonantFrequency =
std::to_string(std::get<VIBRATOR_PARAM_RESONANT_FREQUENCY>(info.param));
std::string qFactor = std::to_string(std::get<VIBRATOR_PARAM_Q_FACTOR>(info.param));
std::string maxAmplitude =
std::to_string(std::get<VIBRATOR_PARAM_MAX_AMPLITUDE>(info.param));
std::string name = getPrefix(descriptor) + "_resonantFrequency" + resonantFrequency +
"_qFactor" + qFactor + "_maxAmplitude" + maxAmplitude;
std::replace_if(
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
return name;
});
/**
* The data tests do the following
* -Generate test input.
* -Check if the parameters are supported. Skip the unsupported parameter values.
* -Validate increase in haptic output energy energy.
**/
enum DataTestParam { EFFECT_INSTANCE, LAYOUT };
using HapticGeneratorDataTestParam = std::tuple<EffectInstance, int32_t>;
class HapticGeneratorDataTest : public ::testing::TestWithParam<HapticGeneratorDataTestParam>,
public HapticGeneratorHelper {
public:
HapticGeneratorDataTest() : mChMask(std::get<LAYOUT>(GetParam())) {
std::tie(mFactory, mDescriptor) = std::get<EFFECT_INSTANCE>(GetParam());
mAudioChannelCount =
getChannelCount(AudioChannelLayout::make<AudioChannelLayout::layoutMask>(mChMask),
~AudioChannelLayout::LAYOUT_HAPTIC_AB);
mHapticChannelCount =
getChannelCount(AudioChannelLayout::make<AudioChannelLayout::layoutMask>(mChMask),
AudioChannelLayout::LAYOUT_HAPTIC_AB);
mAudioSamples = kFrameCount * mAudioChannelCount;
mHapticSamples = kFrameCount * mHapticChannelCount;
mInput.resize(mHapticSamples + mAudioSamples, 0);
mOutput.resize(mHapticSamples + mAudioSamples, 0);
}
void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpHapticGenerator(mChMask)); }
void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownHapticGenerator()); }
void generateSinePeriod() {
size_t cycleSize = kSamplingFrequency / kInputFrequency;
size_t startSize = 0;
while (startSize < mAudioSamples) {
for (size_t i = 0; i < cycleSize; i++) {
mInput[i + startSize] = sin(2 * M_PI * kInputFrequency * i / kSamplingFrequency);
}
startSize += mAudioSamples / 4;
}
}
void setBaseVibratorParam() {
auto vibratorInfo =
createVibratorInfo(kDefaultResonantFrequency, kDefaultQfactor, kDefaultMaxAmp);
if (isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(createVibratorParam(vibratorInfo),
HapticGenerator::vibratorInfo));
} else {
GTEST_SKIP() << "Invalid base vibrator values, skipping the test\n";
}
}
void setBaseScaleParam() {
ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(
createScaleParam({HapticGenerator::HapticScale(kDefaultScaleID, kDefaultScale)}),
HapticGenerator::hapticScales));
}
void validateIncreasingEnergy(HapticGenerator::Tag tag) {
float baseEnergy = -1;
for (auto param : mHapticParam) {
ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(param, tag));
SCOPED_TRACE("Param: " + param.toString());
ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, mOutput, mEffect, &ret));
float hapticOutputEnergy = audio_utils_compute_energy_mono(
mOutput.data() + mAudioSamples, AUDIO_FORMAT_PCM_FLOAT, mHapticSamples);
EXPECT_GT(hapticOutputEnergy, baseEnergy);
baseEnergy = hapticOutputEnergy;
}
}
float findAbsMax(auto begin, auto end) {
return *std::max_element(begin, end,
[](float a, float b) { return std::abs(a) < std::abs(b); });
}
void findMaxAmplitude() {
for (float amp = 0.1; amp <= 1; amp += 0.1) {
auto vibratorInfo = createVibratorInfo(kDefaultResonantFrequency, kDefaultQfactor, amp);
if (!isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo,
mDescriptor)) {
continue;
}
ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(createVibratorParam(vibratorInfo),
HapticGenerator::vibratorInfo));
ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, mOutput, mEffect, &ret));
float outAmplitude = findAbsMax(mOutput.begin() + mAudioSamples, mOutput.end());
if (outAmplitude > mMaxAmplitude) {
mMaxAmplitude = outAmplitude;
} else {
break;
}
}
}
void addHapticScaleParam(int id, HapticGenerator::VibratorScale scale) {
HapticGenerator setHg;
std::vector<HapticGenerator::HapticScale> hapticScales = {{.id = id, .scale = scale}};
setHg.set<HapticGenerator::hapticScales>(hapticScales);
mTags.push_back({HapticGenerator::hapticScales, setHg});
}
void addVibratorInformationParam(float resonantFrequencyHz, float qFactor, float maxAmplitude) {
HapticGenerator hg;
HapticGenerator::VibratorInformation vibrationInfo = {
.resonantFrequencyHz = resonantFrequencyHz,
.qFactor = qFactor,
.maxAmplitude = maxAmplitude};
hg.set<HapticGenerator::vibratorInfo>(vibrationInfo);
mTags.push_back({HapticGenerator::vibratorInfo, hg});
}
private:
enum ParamTestEnum { PARAM_TEST_TAG, PARAM_TEST_TARGET };
std::vector<std::tuple<HapticGenerator::Tag, HapticGenerator>> mTags;
void CleanUp() { mTags.clear(); }
const int kInputFrequency = 1000;
float mMaxAmplitude = 0;
size_t mHapticSamples;
int32_t mChMask;
int32_t mAudioChannelCount;
int32_t mHapticChannelCount;
size_t mAudioSamples;
float mBaseHapticOutputEnergy;
std::vector<Parameter> mHapticParam;
// both input and output buffer includes audio and haptic samples
std::vector<float> mInput;
std::vector<float> mOutput;
};
TEST_P(HapticGeneratorParamTest, SetAndGetHapticScale) {
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(mParamHapticScaleId, mParamVibratorScale));
SetAndGetHapticGeneratorParameters();
TEST_P(HapticGeneratorDataTest, IncreasingVibratorScaleTest) {
generateInput(mInput, kInputFrequency, kSamplingFrequency, mAudioSamples);
ASSERT_NO_FATAL_FAILURE(setBaseVibratorParam());
for (HapticGenerator::VibratorScale scale : kScaleValues) {
mHapticParam.push_back(
createScaleParam({HapticGenerator::HapticScale(kDefaultScaleID, scale)}));
}
ASSERT_NO_FATAL_FAILURE(validateIncreasingEnergy(HapticGenerator::hapticScales));
}
TEST_P(HapticGeneratorParamTest, SetAndGetMultipleHapticScales) {
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(mParamHapticScaleId, mParamVibratorScale));
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(mParamHapticScaleId, mParamVibratorScale));
SetAndGetHapticGeneratorParameters();
TEST_P(HapticGeneratorDataTest, IncreasingMaxAmplitudeTest) {
generateInput(mInput, kInputFrequency, kSamplingFrequency, mAudioSamples);
ASSERT_NO_FATAL_FAILURE(setBaseScaleParam());
findMaxAmplitude();
std::vector<float> increasingAmplitudeValues = {0.25f * mMaxAmplitude, 0.5f * mMaxAmplitude,
0.75f * mMaxAmplitude, mMaxAmplitude};
for (float amplitude : increasingAmplitudeValues) {
auto vibratorInfo =
createVibratorInfo(kDefaultResonantFrequency, kDefaultQfactor, amplitude);
if (!isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
continue;
}
mHapticParam.push_back(createVibratorParam(vibratorInfo));
}
ASSERT_NO_FATAL_FAILURE(validateIncreasingEnergy(HapticGenerator::vibratorInfo));
}
TEST_P(HapticGeneratorParamTest, SetAndGetVibratorInformation) {
EXPECT_NO_FATAL_FAILURE(addVibratorInformationParam(mParamResonantFrequency, mParamQFactor,
mParamMaxAmplitude));
SetAndGetHapticGeneratorParameters();
TEST_P(HapticGeneratorDataTest, DescreasingResonantFrequencyTest) {
std::vector<float> descreasingResonantFrequency = {800, 600, 400, 200};
generateInput(mInput, kInputFrequency, kSamplingFrequency, mAudioSamples);
ASSERT_NO_FATAL_FAILURE(setBaseScaleParam());
for (float resonantFrequency : descreasingResonantFrequency) {
auto vibratorInfo = createVibratorInfo(resonantFrequency, kDefaultQfactor, kDefaultMaxAmp);
if (!isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
continue;
}
mHapticParam.push_back(createVibratorParam(vibratorInfo));
}
ASSERT_NO_FATAL_FAILURE(validateIncreasingEnergy(HapticGenerator::vibratorInfo));
}
TEST_P(HapticGeneratorDataTest, IncreasingQfactorTest) {
std::vector<float> increasingQfactor = {16, 24, 32, 40};
generateSinePeriod();
ASSERT_NO_FATAL_FAILURE(setBaseScaleParam());
for (float qFactor : increasingQfactor) {
auto vibratorInfo = createVibratorInfo(kDefaultResonantFrequency, qFactor, kDefaultMaxAmp);
if (!isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
continue;
}
mHapticParam.push_back(createVibratorParam(vibratorInfo));
}
ASSERT_NO_FATAL_FAILURE(validateIncreasingEnergy(HapticGenerator::vibratorInfo));
}
INSTANTIATE_TEST_SUITE_P(
HapticGeneratorValidTest, HapticGeneratorParamTest,
DataTest, HapticGeneratorDataTest,
::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
IFactory::descriptor, getEffectTypeUuidHapticGenerator())),
testing::ValuesIn(kHapticScaleIdValues),
testing::ValuesIn(kVibratorScaleValues),
testing::ValuesIn(kResonantFrequencyValues),
testing::ValuesIn(kQFactorValues), testing::ValuesIn(kMaxAmplitude)),
[](const testing::TestParamInfo<HapticGeneratorParamTest::ParamType>& info) {
auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
std::string hapticScaleID = std::to_string(std::get<PARAM_HAPTIC_SCALE_ID>(info.param));
std::string hapticScaleVibScale = std::to_string(
static_cast<int>(std::get<PARAM_HAPTIC_SCALE_VIBRATOR_SCALE>(info.param)));
std::string resonantFrequency = std::to_string(
std::get<PARAM_VIBRATION_INFORMATION_RESONANT_FREQUENCY>(info.param));
std::string qFactor =
std::to_string(std::get<PARAM_VIBRATION_INFORMATION_Q_FACTOR>(info.param));
std::string maxAmplitude =
std::to_string(std::get<PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE>(info.param));
std::string name = getPrefix(descriptor) + "_hapticScaleId" + hapticScaleID +
"_hapticScaleVibScale" + hapticScaleVibScale + "_resonantFrequency" +
resonantFrequency + "_qFactor" + qFactor + "_maxAmplitude" +
maxAmplitude;
std::replace_if(
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
testing::ValuesIn(kHapticOutputLayouts)),
[](const testing::TestParamInfo<HapticGeneratorDataTest::ParamType>& info) {
auto descriptor = std::get<EFFECT_INSTANCE>(info.param).second;
std::string layout = "0x" + std::format("{:x}", std::get<LAYOUT>(info.param));
std::string name = getPrefix(descriptor) + "_layout_" + layout;
return name;
});
INSTANTIATE_TEST_SUITE_P(
HapticGeneratorInvalidTest, HapticGeneratorParamTest,
::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
IFactory::descriptor, getEffectTypeUuidHapticGenerator())),
testing::Values(MIN_ID),
testing::Values(HapticGenerator::VibratorScale::NONE),
testing::Values(MIN_FLOAT), testing::Values(MIN_FLOAT),
testing::Values(MIN_FLOAT)),
[](const testing::TestParamInfo<HapticGeneratorParamTest::ParamType>& info) {
auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
std::string hapticScaleID = std::to_string(std::get<PARAM_HAPTIC_SCALE_ID>(info.param));
std::string hapticScaleVibScale = std::to_string(
static_cast<int>(std::get<PARAM_HAPTIC_SCALE_VIBRATOR_SCALE>(info.param)));
std::string resonantFrequency = std::to_string(
std::get<PARAM_VIBRATION_INFORMATION_RESONANT_FREQUENCY>(info.param));
std::string qFactor =
std::to_string(std::get<PARAM_VIBRATION_INFORMATION_Q_FACTOR>(info.param));
std::string maxAmplitude =
std::to_string(std::get<PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE>(info.param));
std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
descriptor.common.name + "_UUID_" +
toString(descriptor.common.id.uuid) + "_hapticScaleId" +
hapticScaleID + "_hapticScaleVibScale" + hapticScaleVibScale +
"_resonantFrequency" + resonantFrequency + "_qFactor" + qFactor +
"_maxAmplitude" + maxAmplitude;
std::replace_if(
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
return name;
});
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorParamTest);
// Test HapticScale[] hapticScales parameter
using HapticGeneratorScalesTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>>;
class HapticGeneratorScalesTest : public ::testing::TestWithParam<HapticGeneratorScalesTestParam>,
public EffectHelper {
public:
HapticGeneratorScalesTest() {
std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
}
void SetUp() override {
ASSERT_NE(nullptr, mFactory);
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
Parameter::Common common = createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
IEffect::OpenEffectReturn ret;
ASSERT_NO_FATAL_FAILURE(open(mEffect, common, std::nullopt, &ret, EX_NONE));
ASSERT_NE(nullptr, mEffect);
}
void TearDown() override {
ASSERT_NO_FATAL_FAILURE(close(mEffect));
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
CleanUp();
}
static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
std::shared_ptr<IFactory> mFactory;
std::shared_ptr<IEffect> mEffect;
Descriptor mDescriptor;
void addHapticScaleParam(std::vector<HapticGenerator::HapticScale> scales) {
mHapticScales.push_back(HapticGenerator::make<HapticGenerator::hapticScales>(scales));
for (const auto& scale : scales) {
expectMap.insert_or_assign(scale.id, scale.scale);
}
}
void SetHapticScaleParameters() {
// std::unordered_set<HapticGenerator::HapticScale> target;
for (auto& it : mHapticScales) {
Parameter::Specific specific =
Parameter::Specific::make<Parameter::Specific::hapticGenerator>(it);
Parameter param = Parameter::make<Parameter::specific>(specific);
EXPECT_STATUS(EX_NONE, mEffect->setParameter(param)) << param.toString();
}
}
void checkHapticScaleParameter() {
// get parameter
Parameter targetParam;
HapticGenerator::Id hgId = HapticGenerator::Id::make<HapticGenerator::Id::commonTag>(
HapticGenerator::hapticScales);
Parameter::Id id = Parameter::Id::make<Parameter::Id::hapticGeneratorTag>(hgId);
EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &targetParam));
ASSERT_EQ(Parameter::specific, targetParam.getTag());
Parameter::Specific specific = targetParam.get<Parameter::specific>();
ASSERT_EQ(Parameter::Specific::hapticGenerator, specific.getTag());
HapticGenerator hg = specific.get<Parameter::Specific::hapticGenerator>();
ASSERT_EQ(HapticGenerator::hapticScales, hg.getTag());
std::vector<HapticGenerator::HapticScale> scales = hg.get<HapticGenerator::hapticScales>();
ASSERT_EQ(scales.size(), expectMap.size());
for (const auto& scale : scales) {
auto itor = expectMap.find(scale.id);
ASSERT_NE(expectMap.end(), itor);
ASSERT_EQ(scale.scale, itor->second);
expectMap.erase(scale.id);
}
ASSERT_EQ(0ul, expectMap.size());
}
const static HapticGenerator::HapticScale kHapticScaleWithMinId;
const static HapticGenerator::HapticScale kHapticScaleWithMinIdNew;
const static HapticGenerator::HapticScale kHapticScale;
const static HapticGenerator::HapticScale kHapticScaleNew;
const static HapticGenerator::HapticScale kHapticScaleWithMaxId;
const static HapticGenerator::HapticScale kHapticScaleWithMaxIdNew;
std::vector<HapticGenerator> mHapticScales;
void CleanUp() {
mHapticScales.clear();
expectMap.clear();
}
private:
std::map<int /* trackID */, HapticGenerator::VibratorScale> expectMap;
};
const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMinId = {
.id = MIN_ID, .scale = HapticGenerator::VibratorScale::MUTE};
const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMinIdNew = {
.id = MIN_ID, .scale = HapticGenerator::VibratorScale::VERY_LOW};
const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScale = {
.id = 1, .scale = HapticGenerator::VibratorScale::LOW};
const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleNew = {
.id = 1, .scale = HapticGenerator::VibratorScale::NONE};
const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMaxId = {
.id = MAX_ID, .scale = HapticGenerator::VibratorScale::VERY_HIGH};
const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMaxIdNew = {
.id = MAX_ID, .scale = HapticGenerator::VibratorScale::MUTE};
TEST_P(HapticGeneratorScalesTest, SetAndUpdateOne) {
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleNew}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMinIdNew}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxIdNew}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
}
TEST_P(HapticGeneratorScalesTest, SetAndUpdateVector) {
EXPECT_NO_FATAL_FAILURE(
addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(
{kHapticScaleNew, kHapticScaleWithMaxIdNew, kHapticScaleWithMinIdNew}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
}
TEST_P(HapticGeneratorScalesTest, SetAndUpdateMultipleVector) {
EXPECT_NO_FATAL_FAILURE(
addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(
{kHapticScaleNew, kHapticScaleWithMaxIdNew, kHapticScaleWithMinIdNew}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(
addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
}
TEST_P(HapticGeneratorScalesTest, SetOneAndAddMoreVector) {
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId, kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
}
TEST_P(HapticGeneratorScalesTest, SetMultipleAndAddOneVector) {
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId, kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
}
TEST_P(HapticGeneratorScalesTest, SetMultipleVectorRepeat) {
EXPECT_NO_FATAL_FAILURE(
addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(
addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(
addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
}
INSTANTIATE_TEST_SUITE_P(
HapticGeneratorScalesTest, HapticGeneratorScalesTest,
::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
IFactory::descriptor, getEffectTypeUuidHapticGenerator()))),
[](const testing::TestParamInfo<HapticGeneratorScalesTest::ParamType>& info) {
auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
descriptor.common.name + "_UUID_" +
toString(descriptor.common.id.uuid);
std::replace_if(
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
return name;
});
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorScalesTest);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorDataTest);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);

View File

@@ -19,10 +19,58 @@
"name": "CtsCarBuiltinApiHostTestCases"
},
{
"name": "CarServiceTest"
"name": "CarServiceAudioTest"
},
{
"name": "CarServiceCarTest"
},
{
"name": "CarServiceClusterTest"
},
{
"name": "CarServiceDiagnosticTest"
},
{
"name": "CarServiceDrivingStateTest"
},
{
"name": "CarServiceEvsTest"
},
{
"name": "CarServiceGarageModeTest"
},
{
"name": "CarServiceInputTest"
},
{
"name": "CarServiceOsTest"
},
{
"name": "CarServicePmTest"
},
{
"name": "CarServicePowerTest"
},
{
"name": "CarServicePropertyTest"
},
{
"name": "CarServiceRemoteAccessTest"
},
{
"name": "CarServiceStorageMonitoringTest"
},
{
"name": "CarServiceTelemetryTest"
},
{
"name": "CarServiceUnitTest"
},
{
"name": "CarServiceVmsTest"
},
{
"name": "CarServiceWatchdogTest"
}
]
}

View File

@@ -46,6 +46,9 @@ aidl_interface {
ndk: {
min_sdk_version: "29",
},
rust: {
enabled: true,
},
},
versions_with_info: [
{

View File

@@ -2,6 +2,6 @@
<hal format="aidl">
<name>android.hardware.automotive.evs</name>
<fqname>IEvsEnumerator/hw/0</fqname>
<version>1</version>
<version>2</version>
</hal>
</manifest>

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2024 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.
*/
rust_binary {
name: "android.hardware.automotive.evs-aidl-rust-service",
relative_install_path: "hw",
vendor: true,
srcs: ["src/*.rs"],
crate_root: "src/main.rs",
vintf_fragments: ["manifest_evs-rust-service.xml"],
init_rc: ["evs-rust-service.rc"],
rustlibs: [
"android.hardware.automotive.evs-V2-rust",
"libbinder_rs",
"liblog_rust",
],
}

View File

@@ -0,0 +1,21 @@
# Rust Skeleton EVS HAL implementation.
WARNING: This is not a reference EVS HAL implementation and therefore does not
provide any actual functionality.
This folder contains a skeleton EVS HAL implementation in Rust to demonstrate
how vendors could implement their EVS HAL in Rust. To compile and run this
implementation, please include below package to the device build script:
* `android.hardware.automotive.evs-aidl-rust-service`
Please note that this service will attempt to register the service as
`IEvsEnumerator/rust/0` and therefore is also required to be declared in the
service context by adding below line to a proper `service_contexts` file:
> android.hardware.automotive.evs.IEvsEnumerator/rust/0 u:object_r:hal_evs_service:s0
This implementation intentionally returns `binder::StatusCode::UNKNOWN_ERROR`
for any API call except deprecated API for ultrasonics; the process will be
panicked on these methods instead. Hence, this implementation does not comply
with VTS tests and vendors must replace each method with actual implementation.

View File

@@ -0,0 +1,8 @@
service vendor.evs-hal-rust-default /vendor/bin/hw/android.hardware.automotive.evs-aidl-rust-service
class early_hal
priority -20
user graphics
group automotive_evs camera
onrestart restart cardisplayproxyd
onrestart restart evsmanagerd
disabled

View File

@@ -0,0 +1,7 @@
<manifest version="2.0" type="device">
<hal format="aidl">
<name>android.hardware.automotive.evs</name>
<version>2</version>
<fqname>IEvsEnumerator/rust/0</fqname>
</hal>
</manifest>

View File

@@ -0,0 +1,113 @@
//
// Copyright (C) 2024 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.
//
use android_hardware_automotive_evs::aidl::android::hardware::automotive::evs::{
CameraDesc::CameraDesc, DisplayState::DisplayState, IEvsCamera::IEvsCamera,
IEvsDisplay::IEvsDisplay, IEvsEnumerator::IEvsEnumerator,
IEvsEnumeratorStatusCallback::IEvsEnumeratorStatusCallback,
IEvsUltrasonicsArray::IEvsUltrasonicsArray, Stream::Stream,
UltrasonicsArrayDesc::UltrasonicsArrayDesc,
};
pub struct DefaultEvsHal {}
impl binder::Interface for DefaultEvsHal {}
impl IEvsEnumerator for DefaultEvsHal {
fn closeCamera(
&self,
_: &binder::Strong<(dyn IEvsCamera + 'static)>,
) -> std::result::Result<(), binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn closeDisplay(
&self,
_: &binder::Strong<(dyn IEvsDisplay + 'static)>,
) -> std::result::Result<(), binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn closeUltrasonicsArray(
&self,
_: &binder::Strong<(dyn IEvsUltrasonicsArray + 'static)>,
) -> std::result::Result<(), binder::Status> {
unimplemented!()
}
fn getCameraList(&self) -> std::result::Result<std::vec::Vec<CameraDesc>, binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn getDisplayIdList(&self) -> std::result::Result<std::vec::Vec<u8>, binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn getDisplayState(&self) -> std::result::Result<DisplayState, binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn getStreamList(
&self,
_: &CameraDesc,
) -> std::result::Result<std::vec::Vec<Stream>, binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn getUltrasonicsArrayList(
&self,
) -> std::result::Result<std::vec::Vec<UltrasonicsArrayDesc>, binder::Status> {
unimplemented!()
}
fn isHardware(&self) -> std::result::Result<bool, binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn openCamera(
&self,
_: &str,
_: &Stream,
) -> std::result::Result<binder::Strong<(dyn IEvsCamera + 'static)>, binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn openDisplay(
&self,
_: i32,
) -> std::result::Result<binder::Strong<(dyn IEvsDisplay + 'static)>, binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn openUltrasonicsArray(
&self,
_: &str,
) -> std::result::Result<binder::Strong<(dyn IEvsUltrasonicsArray + 'static)>, binder::Status>
{
unimplemented!()
}
fn registerStatusCallback(
&self,
_: &binder::Strong<(dyn IEvsEnumeratorStatusCallback + 'static)>,
) -> std::result::Result<(), binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
fn getDisplayStateById(&self, _: i32) -> std::result::Result<DisplayState, binder::Status> {
Err(binder::StatusCode::UNKNOWN_ERROR.into())
}
}

View File

@@ -0,0 +1,42 @@
//
// Copyright (C) 2024 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.
//
mod default_evs_hal;
use crate::default_evs_hal::DefaultEvsHal;
use android_hardware_automotive_evs::aidl::android::hardware::automotive::evs::IEvsEnumerator::BnEvsEnumerator;
use log::info;
fn main() {
binder::ProcessState::start_thread_pool();
let service = DefaultEvsHal {};
// Register HAL implementation as rust/0 instance.
let service_name = "android.hardware.automotive.evs.IEvsEnumerator/rust/0";
let service_binder = BnEvsEnumerator::new_binder(service, binder::BinderFeatures::default());
binder::add_service(service_name, service_binder.as_binder())
.expect(format!("Failed to register {}.", service_name).as_str());
info!("EVS Hardware Enumerator is ready");
binder::ProcessState::join_thread_pool();
// In normal operation, we don't expect the thread pool to exit.
info!("EVS Hardware Enumerator is shutting down");
}

View File

@@ -22,7 +22,7 @@ cc_defaults {
name: "VehicleHalInterfaceDefaults",
static_libs: [
"android.hardware.automotive.vehicle-V3-ndk",
"android.hardware.automotive.vehicle.property-V3-ndk",
"android.hardware.automotive.vehicle.property-V4-ndk",
],
}
@@ -30,6 +30,14 @@ rust_defaults {
name: "VehicleHalInterfaceRustDefaults",
rustlibs: [
"android.hardware.automotive.vehicle-V3-rust",
"android.hardware.automotive.vehicle.property-V3-rust",
"android.hardware.automotive.vehicle.property-V4-rust",
],
}
aidl_interface_defaults {
name: "android.hardware.automotive.vehicle-latest-defaults",
imports: [
"android.hardware.automotive.vehicle-V3",
"android.hardware.automotive.vehicle.property-V4",
],
}

View File

@@ -40,7 +40,7 @@ cc_test {
cc_test {
name: "VehiclePropertyAnnotationCppTest",
srcs: ["VehiclePropertyAnnotationCppTest.cpp"],
header_libs: ["IVehicleGeneratedHeaders-V3"],
header_libs: ["IVehicleGeneratedHeaders-V4"],
defaults: ["VehicleHalInterfaceDefaults"],
test_suites: ["general-tests"],
}
@@ -49,11 +49,11 @@ android_test {
name: "VehiclePropertyAnnotationJavaTest",
srcs: [
"VehiclePropertyAnnotationJavaTest.java",
":IVehicleGeneratedJavaFiles-V3",
":IVehicleGeneratedJavaFiles-V4",
],
static_libs: [
"android.hardware.automotive.vehicle-V3-java",
"android.hardware.automotive.vehicle.property-V3-java",
"android.hardware.automotive.vehicle.property-V4-java",
"androidx.test.runner",
"truth",
],

View File

@@ -27,6 +27,10 @@
#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
#include <aidl/android/hardware/automotive/vehicle/VehiclePropertyAccess.h>
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
#include <PerDisplayMaxBrightness.h>
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
#include <unordered_map>
namespace aidl {
@@ -302,6 +306,9 @@ std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehiclePrope
{VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyAccess::READ},
{VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess::READ},
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
{PER_DISPLAY_MAX_BRIGHTNESS, VehiclePropertyAccess::READ},
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
};
} // namespace vehicle

View File

@@ -27,6 +27,10 @@
#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
#include <aidl/android/hardware/automotive/vehicle/VehiclePropertyChangeMode.h>
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
#include <PerDisplayMaxBrightness.h>
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
#include <unordered_map>
namespace aidl {
@@ -302,6 +306,9 @@ std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehi
{VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
{PER_DISPLAY_MAX_BRIGHTNESS, VehiclePropertyChangeMode::STATIC},
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
};
} // namespace vehicle

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 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/vehicle/VehicleProperty.h>
namespace aidl::android::hardware::automotive::vehicle {
// Same as VehicleProperty::PER_DISPLAY_MAX_BRIGHTNESS as defined in v4.
static constexpr VehicleProperty PER_DISPLAY_MAX_BRIGHTNESS = (VehicleProperty)0x11410F4E;
} // namespace aidl::android::hardware::automotive::vehicle

View File

@@ -26,6 +26,10 @@
#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
#include <PerDisplayMaxBrightness.h>
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
#include <unordered_map>
namespace aidl {
@@ -301,6 +305,9 @@ std::unordered_map<VehicleProperty, int32_t> VersionForVehicleProperty = {
{VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE, 3},
{VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, 3},
{VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, 3},
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
{PER_DISPLAY_MAX_BRIGHTNESS, 2},
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
};
} // namespace vehicle

View File

@@ -28,6 +28,10 @@ import java.util.Map;
public final class AccessForVehicleProperty {
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
private static final int PER_DISPLAY_MAX_BRIGHTNESS = 0x11410F4E;
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
public static final Map<Integer, Integer> values = Map.ofEntries(
Map.entry(VehicleProperty.INFO_VIN, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.INFO_MAKE, VehiclePropertyAccess.READ),
@@ -294,7 +298,10 @@ public final class AccessForVehicleProperty {
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess.READ)
Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess.READ),
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
Map.entry(PER_DISPLAY_MAX_BRIGHTNESS, VehiclePropertyAccess.READ)
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
);
}

View File

@@ -28,6 +28,10 @@ import java.util.Map;
public final class ChangeModeForVehicleProperty {
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
private static final int PER_DISPLAY_MAX_BRIGHTNESS = 0x11410F4E;
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
public static final Map<Integer, Integer> values = Map.ofEntries(
Map.entry(VehicleProperty.INFO_VIN, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.INFO_MAKE, VehiclePropertyChangeMode.STATIC),
@@ -294,7 +298,10 @@ public final class ChangeModeForVehicleProperty {
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode.ON_CHANGE)
Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
Map.entry(PER_DISPLAY_MAX_BRIGHTNESS, VehiclePropertyChangeMode.STATIC)
// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
);
}

View File

@@ -27,7 +27,7 @@ cc_library {
defaults: ["VehicleHalDefaults"],
static_libs: ["VehicleHalUtils"],
header_libs: [
"IVehicleGeneratedHeaders-V3",
"IVehicleGeneratedHeaders-V4",
],
shared_libs: ["libjsoncpp"],
}
@@ -44,7 +44,7 @@ cc_library {
defaults: ["VehicleHalDefaults"],
static_libs: ["VehicleHalUtils"],
header_libs: [
"IVehicleGeneratedHeaders-V3",
"IVehicleGeneratedHeaders-V4",
"libbinder_headers",
],
cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
@@ -60,7 +60,7 @@ cc_library_headers {
defaults: ["VehicleHalDefaults"],
static_libs: ["VehicleHalUtils"],
header_libs: [
"IVehicleGeneratedHeaders-V3",
"IVehicleGeneratedHeaders-V4",
],
shared_libs: ["libjsoncpp"],
}

View File

@@ -27,8 +27,6 @@ cc_test {
"VehicleHalJsonConfigLoader",
"VehicleHalUtils",
"libgtest",
],
shared_libs: [
"libjsoncpp",
],
defaults: ["VehicleHalDefaults"],
@@ -43,8 +41,6 @@ cc_test {
"VehicleHalJsonConfigLoaderEnableTestProperties",
"VehicleHalUtils",
"libgtest",
],
shared_libs: [
"libjsoncpp",
],
defaults: ["VehicleHalDefaults"],

View File

@@ -0,0 +1,8 @@
{
"ravenwood-presubmit": [
{
"name": "CarServiceHostUnitTest",
"host": true
}
]
}

View File

@@ -3195,19 +3195,22 @@
}
},
{
"property": "VehicleProperty::DISPLAY_BRIGHTNESS",
"property": "VehicleProperty::PER_DISPLAY_BRIGHTNESS"
},
{
"property": "VehicleProperty::PER_DISPLAY_MAX_BRIGHTNESS",
"defaultValue": {
"int32Values": [
0,
100,
1,
100,
2,
100,
3,
100
]
},
"areas": [
{
"areaId": 0,
"minInt32Value": 0,
"maxInt32Value": 100
}
]
}
},
{
"property": "VehicleProperty::VALET_MODE_ENABLED",

View File

@@ -29,12 +29,10 @@ cc_test {
"VehicleHalUtils",
"libgmock",
"libgtest",
"libjsoncpp",
],
header_libs: [
"IVehicleGeneratedHeaders-V3",
],
shared_libs: [
"libjsoncpp",
"IVehicleGeneratedHeaders-V4",
],
data: [
":VehicleHalDefaultProperties_JSON",
@@ -52,15 +50,13 @@ cc_test {
"VehicleHalUtils",
"libgmock",
"libgtest",
"libjsoncpp",
],
cflags: [
"-DENABLE_VEHICLE_HAL_TEST_PROPERTIES",
],
header_libs: [
"IVehicleGeneratedHeaders-V3",
],
shared_libs: [
"libjsoncpp",
"IVehicleGeneratedHeaders-V4",
],
data: [
":VehicleHalDefaultProperties_JSON",

View File

@@ -28,8 +28,6 @@ cc_test {
"VehicleHalUtils",
"FakeVehicleHalValueGenerators",
"FakeObd2Frame",
],
shared_libs: [
"libjsoncpp",
],
data: [

View File

@@ -1056,6 +1056,10 @@ VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu
VhalResult<void> isAdasPropertyAvailableResult;
VhalResult<bool> isCruiseControlTypeStandardResult;
switch (propId) {
case toInt(VehicleProperty::DISPLAY_BRIGHTNESS):
case toInt(VehicleProperty::PER_DISPLAY_BRIGHTNESS):
ALOGD("DISPLAY_BRIGHTNESS: %s", value.toString().c_str());
return {};
case toInt(VehicleProperty::AP_POWER_STATE_REPORT):
*isSpecialValue = true;
return setApPowerStateReport(value);

View File

@@ -40,10 +40,10 @@ cc_test {
"FakeUserHal",
"libgtest",
"libgmock",
"libjsoncpp",
],
shared_libs: [
"libgrpc++",
"libjsoncpp",
"libprotobuf-cpp-full",
],
data: [

View File

@@ -83,6 +83,17 @@ std::vector<aidlvhal::VehiclePropConfig> GRPCVehicleHardware::getAllPropertyConf
return configs;
}
std::optional<aidlvhal::VehiclePropConfig> GRPCVehicleHardware::getPropertyConfig(
int32_t propId) const {
// TODO(b/354055835): Use GRPC call to get one config instead of getting all the configs.
for (const auto& config : getAllPropertyConfigs()) {
if (config.prop == propId) {
return config;
}
}
return std::nullopt;
}
aidlvhal::StatusCode GRPCVehicleHardware::setValues(
std::shared_ptr<const SetValuesCallback> callback,
const std::vector<aidlvhal::SetValueRequest>& requests) {
@@ -265,6 +276,7 @@ DumpResult GRPCVehicleHardware::dump(const std::vector<std::string>& options) {
return {
.callerShouldDumpState = protoDumpResult.caller_should_dump_state(),
.buffer = protoDumpResult.buffer(),
.refreshPropertyConfigs = protoDumpResult.refresh_property_configs(),
};
}

View File

@@ -50,6 +50,10 @@ class GRPCVehicleHardware : public IVehicleHardware {
// Get all the property configs.
std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const override;
// Get the config for the specified propId.
std::optional<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
getPropertyConfig(int32_t propId) const override;
// Set property values asynchronously. Server could return before the property set requests
// are sent to vehicle bus or before property set confirmation is received. The callback is
// safe to be called after the function returns and is safe to be called in a different thread.

View File

@@ -226,6 +226,7 @@ GrpcVehicleProxyServer::GrpcVehicleProxyServer(std::vector<std::string> serverAd
auto dumpResult = mHardware->dump(dumpOptionStrings);
result->set_caller_should_dump_state(dumpResult.callerShouldDumpState);
result->set_buffer(dumpResult.buffer);
result->set_refresh_property_configs(dumpResult.refreshPropertyConfigs);
return ::grpc::Status::OK;
}

View File

@@ -20,6 +20,7 @@
#include <VehicleHalTypes.h>
#include <memory>
#include <optional>
#include <vector>
namespace android {
@@ -46,33 +47,53 @@ struct SetValueErrorEvent {
int32_t areaId;
};
namespace aidlvhal = ::aidl::android::hardware::automotive::vehicle;
// An abstract interface to access vehicle hardware.
// For virtualized VHAL, GrpcVehicleHardware would communicate with a VehicleHardware
// implementation in another VM through GRPC. For non-virtualzied VHAL, VHAL directly communicates
// with a VehicleHardware through this interface.
class IVehicleHardware {
public:
using SetValuesCallback = std::function<void(
std::vector<aidl::android::hardware::automotive::vehicle::SetValueResult>)>;
using GetValuesCallback = std::function<void(
std::vector<aidl::android::hardware::automotive::vehicle::GetValueResult>)>;
using PropertyChangeCallback = std::function<void(
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>)>;
using SetValuesCallback = std::function<void(std::vector<aidlvhal::SetValueResult>)>;
using GetValuesCallback = std::function<void(std::vector<aidlvhal::GetValueResult>)>;
using PropertyChangeCallback = std::function<void(std::vector<aidlvhal::VehiclePropValue>)>;
using PropertySetErrorCallback = std::function<void(std::vector<SetValueErrorEvent>)>;
virtual ~IVehicleHardware() = default;
// Get all the property configs.
virtual std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
getAllPropertyConfigs() const = 0;
virtual std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const = 0;
// Get the property configs for the specified propId. This is used for early-boot
// native VHAL clients to access certain property configs when not all property configs are
// available. For example, a config discovery process might be required to determine the
// property config for HVAC. However, for early boot properties, e.g. VHAL_HEARTBEAT, it
// could return before the config discovery process.
//
// Currently Android system may try to access the following properties during early boot:
// STORAGE_ENCRYPTION_BINDING_SEED, WATCHDOG_ALIVE, WATCHDOG_TERMINATE_PROCESS, VHAL_HEARTBEAT,
// CURRENT_POWER_POLICY, POWER_POLICY_REQ, POWER_POLICY_GROUP_REQ. They should return
// quickly otherwise the whole bootup process might be blocked.
virtual std::optional<aidlvhal::VehiclePropConfig> getPropertyConfig(int32_t propId) const {
// The default implementation is to use getAllPropertyConfigs(). This should be
// overridden if getAllPropertyConfigs() takes a while to return for initial boot or
// relies on ethernet or other communication channel that is not available during early
// boot.
for (const auto& config : getAllPropertyConfigs()) {
if (config.prop == propId) {
return config;
}
}
return std::nullopt;
}
// Set property values asynchronously. Server could return before the property set requests
// are sent to vehicle bus or before property set confirmation is received. The callback is
// safe to be called after the function returns and is safe to be called in a different thread.
virtual aidl::android::hardware::automotive::vehicle::StatusCode setValues(
virtual aidlvhal::StatusCode setValues(
std::shared_ptr<const SetValuesCallback> callback,
const std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>&
requests) = 0;
const std::vector<aidlvhal::SetValueRequest>& requests) = 0;
// Get property values asynchronously. Server could return before the property values are ready.
// The callback is safe to be called after the function returns and is safe to be called in a
@@ -86,7 +107,7 @@ class IVehicleHardware {
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;
virtual aidlvhal::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.
@@ -179,16 +200,14 @@ class IVehicleHardware {
// 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;
virtual aidlvhal::StatusCode subscribe([[maybe_unused]] aidlvhal::SubscribeOptions options) {
return aidlvhal::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;
virtual aidlvhal::StatusCode unsubscribe([[maybe_unused]] int32_t propId,
[[maybe_unused]] int32_t areaId) {
return aidlvhal::StatusCode::OK;
}
// This function is deprecated, subscribe/unsubscribe should be used instead.
@@ -216,10 +235,10 @@ class IVehicleHardware {
//
// If the impl is always polling at {@code maxSampleRate} as specified in config, then this
// function can be a no-op.
virtual aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
[[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId,
[[maybe_unused]] float sampleRate) {
return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
virtual aidlvhal::StatusCode updateSampleRate([[maybe_unused]] int32_t propId,
[[maybe_unused]] int32_t areaId,
[[maybe_unused]] float sampleRate) {
return aidlvhal::StatusCode::OK;
}
};

View File

@@ -106,3 +106,21 @@ cc_library_static {
"-Wno-unused-parameter",
],
}
rust_protobuf {
name: "libvehicle_hal_property_protos",
crate_name: "vehicle_hal_property_protos",
protos: [":VehicleHalProtoFiles"],
source_stem: "vehicle_hal_property_protos",
host_supported: true,
vendor_available: true,
product_available: true,
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
],
exported_include_dirs: ["."],
proto_flags: [
"-I external/protobuf/src",
],
}

View File

@@ -25,4 +25,6 @@ message DumpResult {
bool caller_should_dump_state = 1;
/* The dumped information for the caller to print. */
string buffer = 2;
/* To pass if DefaultVehicleHal should refresh the property configs. */
bool refresh_property_configs = 3;
}

View File

@@ -66,7 +66,7 @@ cc_library {
],
header_libs: [
"IVehicleHardware",
"IVehicleGeneratedHeaders-V3",
"IVehicleGeneratedHeaders-V4",
],
shared_libs: [
"libbinder_ndk",

View File

@@ -199,6 +199,8 @@ class DefaultVehicleHal final : public aidlvhal::BnVehicle {
bool checkDumpPermission();
bool isConfigSupportedForCurrentVhalVersion(const aidlvhal::VehiclePropConfig& config) const;
bool getAllPropConfigsFromHardwareLocked() const EXCLUDES(mConfigLock);
// The looping handler function to process all onBinderDied or onBinderUnlinked events in

View File

@@ -340,32 +340,37 @@ int32_t DefaultVehicleHal::getVhalInterfaceVersion() const {
return myVersion;
}
bool DefaultVehicleHal::isConfigSupportedForCurrentVhalVersion(
const VehiclePropConfig& config) const {
int32_t myVersion = getVhalInterfaceVersion();
if (!isSystemProp(config.prop)) {
return true;
}
VehicleProperty property = static_cast<VehicleProperty>(config.prop);
std::string propertyName = aidl::android::hardware::automotive::vehicle::toString(property);
auto it = VersionForVehicleProperty.find(property);
if (it == VersionForVehicleProperty.end()) {
ALOGE("The property: %s is not a supported system property, ignore", propertyName.c_str());
return false;
}
int requiredVersion = it->second;
if (myVersion < requiredVersion) {
ALOGE("The property: %s is not supported for current client VHAL version, "
"require %d, current version: %d, ignore",
propertyName.c_str(), requiredVersion, myVersion);
return false;
}
return true;
}
bool DefaultVehicleHal::getAllPropConfigsFromHardwareLocked() const {
ALOGD("Get all property configs from hardware");
auto configs = mVehicleHardware->getAllPropertyConfigs();
std::vector<VehiclePropConfig> filteredConfigs;
int32_t myVersion = getVhalInterfaceVersion();
for (auto& config : configs) {
if (!isSystemProp(config.prop)) {
for (const auto& config : configs) {
if (isConfigSupportedForCurrentVhalVersion(config)) {
filteredConfigs.push_back(std::move(config));
continue;
}
VehicleProperty property = static_cast<VehicleProperty>(config.prop);
std::string propertyName = aidl::android::hardware::automotive::vehicle::toString(property);
auto it = VersionForVehicleProperty.find(property);
if (it == VersionForVehicleProperty.end()) {
ALOGE("The property: %s is not a supported system property, ignore",
propertyName.c_str());
continue;
}
int requiredVersion = it->second;
if (myVersion < requiredVersion) {
ALOGE("The property: %s is not supported for current client VHAL version, "
"require %d, current version: %d, ignore",
propertyName.c_str(), requiredVersion, myVersion);
continue;
}
filteredConfigs.push_back(std::move(config));
}
{
@@ -431,6 +436,19 @@ ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs* output) {
Result<VehiclePropConfig> DefaultVehicleHal::getConfig(int32_t propId) const {
Result<VehiclePropConfig> result;
if (!mConfigInit) {
std::optional<VehiclePropConfig> config = mVehicleHardware->getPropertyConfig(propId);
if (!config.has_value()) {
return Error() << "no config for property, ID: " << propId;
}
if (!isConfigSupportedForCurrentVhalVersion(config.value())) {
return Error() << "property not supported for current VHAL interface, ID: " << propId;
}
return config.value();
}
getConfigsByPropId([this, &result, propId](const auto& configsByPropId) {
SharedScopedLockAssertion lockAssertion(mConfigLock);
@@ -685,6 +703,22 @@ ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,
ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>& props,
VehiclePropConfigs* output) {
std::vector<VehiclePropConfig> configs;
if (!mConfigInit) {
for (int32_t prop : props) {
auto maybeConfig = mVehicleHardware->getPropertyConfig(prop);
if (!maybeConfig.has_value() ||
!isConfigSupportedForCurrentVhalVersion(maybeConfig.value())) {
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
toInt(StatusCode::INVALID_ARG),
StringPrintf("no config for property, ID: %" PRId32, prop).c_str());
}
configs.push_back(maybeConfig.value());
}
return vectorToStableLargeParcelable(std::move(configs), output);
}
ScopedAStatus status = ScopedAStatus::ok();
getConfigsByPropId([this, &configs, &status, &props](const auto& configsByPropId) {
SharedScopedLockAssertion lockAssertion(mConfigLock);

View File

@@ -650,6 +650,8 @@ TEST_F(DefaultVehicleHalTest, testGetPropConfigs) {
auto hardware = std::make_unique<MockVehicleHardware>();
hardware->setPropertyConfigs(testConfigs);
// Store the pointer for testing. We are sure it is valid.
MockVehicleHardware* hardwarePtr = hardware.get();
auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
@@ -658,6 +660,7 @@ TEST_F(DefaultVehicleHalTest, testGetPropConfigs) {
ASSERT_TRUE(status.isOk()) << "getPropConfigs failed: " << status.getMessage();
ASSERT_EQ(output.payloads, testConfigs);
ASSERT_FALSE(hardwarePtr->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testGetPropConfigsInvalidArg) {
@@ -704,6 +707,34 @@ TEST_F(DefaultVehicleHalTest, testGetValuesSmall) {
ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback";
EXPECT_EQ(maybeGetValueResults.value().payloads, expectedResults) << "results mismatch";
EXPECT_EQ(countClients(), static_cast<size_t>(1));
ASSERT_FALSE(getHardware()->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testGetValuesSmall_AfterGetAllPropConfigs) {
GetValueRequests requests;
std::vector<GetValueResult> expectedResults;
std::vector<GetValueRequest> expectedHardwareRequests;
// If we already called getAllPropConfigs, the configs will be cached.
VehiclePropConfigs output;
getClient()->getAllPropConfigs(&output);
ASSERT_TRUE(getValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok());
getHardware()->addGetValueResponses(expectedResults);
auto status = getClient()->getValues(getCallbackClient(), requests);
ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage();
EXPECT_EQ(getHardware()->nextGetValueRequests(), expectedHardwareRequests)
<< "requests to hardware mismatch";
auto maybeGetValueResults = getCallback()->nextGetValueResults();
ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback";
EXPECT_EQ(maybeGetValueResults.value().payloads, expectedResults) << "results mismatch";
EXPECT_EQ(countClients(), static_cast<size_t>(1));
ASSERT_TRUE(getHardware()->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testGetValuesLarge) {
@@ -1016,6 +1047,34 @@ TEST_F(DefaultVehicleHalTest, testSetValuesSmall) {
ASSERT_TRUE(maybeSetValueResults.has_value()) << "no results in callback";
ASSERT_EQ(maybeSetValueResults.value().payloads, expectedResults) << "results mismatch";
EXPECT_EQ(countClients(), static_cast<size_t>(1));
ASSERT_FALSE(getHardware()->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testSetValuesSmall_AfterGetAllPropConfigs) {
SetValueRequests requests;
std::vector<SetValueResult> expectedResults;
std::vector<SetValueRequest> expectedHardwareRequests;
// If we already called getAllPropConfigs, the configs will be cached.
VehiclePropConfigs output;
getClient()->getAllPropConfigs(&output);
ASSERT_TRUE(setValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok());
getHardware()->addSetValueResponses(expectedResults);
auto status = getClient()->setValues(getCallbackClient(), requests);
ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
EXPECT_EQ(getHardware()->nextSetValueRequests(), expectedHardwareRequests)
<< "requests to hardware mismatch";
auto maybeSetValueResults = getCallback()->nextSetValueResults();
ASSERT_TRUE(maybeSetValueResults.has_value()) << "no results in callback";
ASSERT_EQ(maybeSetValueResults.value().payloads, expectedResults) << "results mismatch";
EXPECT_EQ(countClients(), static_cast<size_t>(1));
ASSERT_TRUE(getHardware()->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testSetValuesLarge) {

View File

@@ -45,9 +45,20 @@ MockVehicleHardware::~MockVehicleHardware() {
std::vector<VehiclePropConfig> MockVehicleHardware::getAllPropertyConfigs() const {
std::scoped_lock<std::mutex> lockGuard(mLock);
mGetAllPropertyConfigsCalled = true;
return mPropertyConfigs;
}
std::optional<VehiclePropConfig> MockVehicleHardware::getPropertyConfig(int32_t propId) const {
std::scoped_lock<std::mutex> lockGuard(mLock);
for (const auto& config : mPropertyConfigs) {
if (config.prop == propId) {
return config;
}
}
return std::nullopt;
}
StatusCode MockVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback,
const std::vector<SetValueRequest>& requests) {
std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -336,6 +347,11 @@ void MockVehicleHardware::sendOnPropertySetErrorEvent(
(*mPropertySetErrorCallback)(errorEvents);
}
bool MockVehicleHardware::getAllPropertyConfigsCalled() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mGetAllPropertyConfigsCalled;
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware

View File

@@ -47,6 +47,8 @@ class MockVehicleHardware final : public IVehicleHardware {
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
getAllPropertyConfigs() const override;
std::optional<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
getPropertyConfig(int32_t propId) const override;
aidl::android::hardware::automotive::vehicle::StatusCode setValues(
std::shared_ptr<const SetValuesCallback> callback,
const std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>&
@@ -98,6 +100,9 @@ class MockVehicleHardware final : public IVehicleHardware {
std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>
getSubscribeOptions();
void clearSubscribeOptions();
// Whether getAllPropertyConfigs() has been called, which blocks all all property configs
// being ready.
bool getAllPropertyConfigsCalled();
private:
mutable std::mutex mLock;
@@ -143,6 +148,8 @@ class MockVehicleHardware final : public IVehicleHardware {
DumpResult mDumpResult;
mutable bool mGetAllPropertyConfigsCalled GUARDED_BY(mLock) = false;
// RecurrentTimer is thread-safe.
std::shared_ptr<RecurrentTimer> mRecurrentTimer;
std::unordered_map<int32_t, std::unordered_map<int32_t, std::shared_ptr<std::function<void()>>>>

View File

@@ -79,17 +79,18 @@ public final class EmuMetadataGenerator {
+ "either this or input_files must be specified\n" + INPUT_FILES_OPTION
+ ": one or more Java files, this is used to decide the input "
+ "directory\n" + PACKAGE_NAME_OPTION
+ ": the optional package name for the interface, by default is " + DEFAULT_PACKAGE_NAME
+ "\n" + OUTPUT_JSON_OPTION + ": The output JSON file\n" + OUTPUT_EMPTY_FILE_OPTION
+ ": Only used for check_mode, this file will be created if "
+ ": the optional package name for the interface, by default is "
+ DEFAULT_PACKAGE_NAME + "\n" + OUTPUT_JSON_OPTION + ": The output JSON file\n"
+ OUTPUT_EMPTY_FILE_OPTION + ": Only used for check_mode, this file will be created if "
+ "check passed\n" + CHECK_AGAINST_OPTION
+ ": An optional JSON file to check against. If specified, the "
+ "generated output file will be checked against this file, if they are not the same, "
+ ("generated output file will be checked against this file, if they are not the "
+ "same, ")
+ "the script will fail, otherwise, the output_empty_file will be created\n"
+ "For example: \n"
+ "EnumMetadataGenerator --input_dir out/soong/.intermediates/hardware/"
+ "interfaces/automotive/vehicle/aidl_property/android.hardware.automotive.vehicle."
+ "property-V3-java-source/gen/ --package_name android.hardware.automotive.vehicle "
+ "property-V4-java-source/gen/ --package_name android.hardware.automotive.vehicle "
+ "--output_json /tmp/android.hardware.automotive.vehicle-types-meta.json";
private static final String VEHICLE_PROPERTY_FILE = "VehicleProperty.java";
private static final String CHECK_FILE_PATH =

View File

@@ -17,4 +17,4 @@
LOCAL_STATIC_LIBRARIES += \
android.hardware.automotive.vehicle-V3-ndk \
android.hardware.automotive.vehicle.property-V3-ndk
android.hardware.automotive.vehicle.property-V4-ndk

View File

@@ -44,7 +44,7 @@ cc_test {
"vhalclient_defaults",
],
header_libs: [
"IVehicleGeneratedHeaders-V3",
"IVehicleGeneratedHeaders-V4",
],
test_suites: [
"general-tests",

View File

@@ -22,6 +22,12 @@ aidl_interface {
cpp: {
enabled: false,
},
ndk: {
apex_available: [
"//apex_available:anyapex",
"//apex_available:platform",
],
},
rust: {
enabled: true,
},

View File

@@ -22,7 +22,7 @@ cc_library {
// SPDX-license-identifier-Apache-2.0
name: "android.hardware.biometrics.common.config",
export_include_dirs: ["include"],
vendor: true,
vendor_available: true,
srcs: [
"Config.cpp",
],
@@ -30,6 +30,10 @@ cc_library {
"libbase",
"libbinder_ndk",
],
apex_available: [
"//apex_available:anyapex",
"//apex_available:platform",
],
}
cc_test_host {

View File

@@ -100,7 +100,11 @@ class Config {
} else if (std::holds_alternative<OptIntVec>(v)) {
for (auto x : std::get<OptIntVec>(v))
if (x.has_value()) os << x.value() << " ";
} else if (std::holds_alternative<OptString>(v)) {
OptString ov = std::get<OptString>(v);
if (ov.has_value()) os << ov.value();
}
return os.str();
}
std::string toString() const {

View File

@@ -10,10 +10,14 @@ cc_library {
// SPDX-license-identifier-Apache-2.0
name: "android.hardware.biometrics.common.thread",
export_include_dirs: ["include"],
vendor: true,
vendor_available: true,
srcs: [
"WorkerThread.cpp",
],
apex_available: [
"//apex_available:anyapex",
"//apex_available:platform",
],
}
cc_test_host {

View File

@@ -6,7 +6,7 @@ cc_library {
// SPDX-license-identifier-Apache-2.0
name: "android.hardware.biometrics.common.util",
export_include_dirs: ["include"],
vendor: true,
vendor_available: true,
srcs: [
"CancellationSignal.cpp",
],
@@ -15,4 +15,8 @@ cc_library {
"libbinder_ndk",
"android.hardware.biometrics.common-V4-ndk",
],
apex_available: [
"//apex_available:anyapex",
"//apex_available:platform",
],
}

View File

@@ -11,7 +11,7 @@ aidl_interface {
name: "android.hardware.biometrics.face",
vendor_available: true,
srcs: [
"android/hardware/biometrics/face/**/*.aidl",
"android/hardware/biometrics/face/*.aidl",
],
imports: [
"android.hardware.biometrics.common-V4",
@@ -36,6 +36,10 @@ aidl_interface {
additional_shared_libraries: [
"libnativewindow",
],
apex_available: [
"//apex_available:platform",
"com.android.hardware.biometrics.face.virtual",
],
},
},
versions_with_info: [
@@ -74,5 +78,39 @@ aidl_interface {
],
frozen: true,
}
aidl_interface {
name: "android.hardware.biometrics.face.virtualhal",
srcs: [
"android/hardware/biometrics/face/virtualhal/*.aidl",
],
imports: [
"android.hardware.biometrics.common-V4",
"android.hardware.keymaster-V4",
"android.hardware.biometrics.face-V4",
],
vendor_available: true,
unstable: true,
backend: {
java: {
platform_apis: true,
},
rust: {
enabled: false,
},
cpp: {
enabled: false,
},
ndk: {
additional_shared_libraries: [
"libnativewindow",
],
apex_available: [
"com.android.hardware.biometrics.face.virtual",
"//apex_available:platform",
],
},
},
frozen: false,
}

View File

@@ -73,7 +73,7 @@ interface ISession {
* Note that this interface allows multiple in-flight challenges. Invoking generateChallenge
* twice does not invalidate the first challenge. The challenge is invalidated only when:
* 1) Its lifespan exceeds the challenge timeout defined in the TEE.
* 2) IFingerprint#revokeChallenge is invoked
* 2) IFace#revokeChallenge is invoked
*
* For example, the following is a possible table of valid challenges:
* ----------------------------------------------

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2024 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.biometrics.face.virtualhal;
import android.hardware.biometrics.face.AcquiredInfo;
/**
* @hide
*/
union AcquiredInfoAndVendorCode {
/**
* Acquired info as specified in AcqauiredInfo.aidl
*/
AcquiredInfo acquiredInfo = AcquiredInfo.UNKNOWN;
/**
* Vendor specific code
*/
int vendorCode;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2024 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.biometrics.face.virtualhal;
import android.hardware.biometrics.face.virtualhal.AcquiredInfoAndVendorCode;
/**
* @hide
*/
parcelable EnrollmentProgressStep {
/**
* The duration of the enrollment step in milli-seconds
*/
int durationMs;
/**
* The sequence of acquired info and vendor code to be issued by HAL during the step.
* The codes are evenly spread over the duration
*/
AcquiredInfoAndVendorCode[] acquiredInfoAndVendorCodes;
}

View File

@@ -0,0 +1,297 @@
/*
* Copyright (C) 2024 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.biometrics.face.virtualhal;
import android.hardware.biometrics.common.SensorStrength;
import android.hardware.biometrics.face.FaceSensorType;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.virtualhal.AcquiredInfoAndVendorCode;
import android.hardware.biometrics.face.virtualhal.NextEnrollment;
/**
* @hide
*/
interface IVirtualHal {
/**
* The operation failed due to invalid input parameters, the error messages should
* gives more details
*/
const int STATUS_INVALID_PARAMETER = 1;
/**
* Set Face Virtual HAL behavior parameters
*/
/**
* setEnrollments
*
* Set the ids of the faces that were currently enrolled in the Virtual HAL,
*
* @param ids ids can contain 1 or more ids, each must be larger than 0
*/
void setEnrollments(in int[] id);
/**
* setEnrollmentHit
*
* Set current face enrollment ids in Face Virtual HAL,
*
* @param ids ids can contain 1 or more ids, each must be larger than 0
*/
void setEnrollmentHit(in int hit_id);
/**
* setNextEnrollment
*
* Set the next enrollment behavior
*
* @param next_enrollment specifies enrollment id, progress stages and final result
*/
void setNextEnrollment(in NextEnrollment next_enrollment);
/**
* setAuthenticatorId
*
* Set authenticator id in virtual HAL, the id is returned in ISession#AuthenticatorId() call
*
* @param id authenticator id value, only applied to the sensor with SensorStrength::STRONG.
*/
void setAuthenticatorId(in long id);
/**
* setChallenge
*
* Set the challenge generated by the virtual HAL, which is returned in
* ISessionCallback#onChallengeGenerated()
*
* @param challenge
*/
void setChallenge(in long challenge);
/**
* setOperationAuthenticateFails
*
* Set whether to force authentication to fail. If true, the virtual hal will report failure on
* authentication attempt until it is set to false
*
* @param fail if true, then the next authentication will fail
*/
void setOperationAuthenticateFails(in boolean fail);
/**
* setOperationAuthenticateLatency
*
* Set authentication latency in the virtual hal in a fixed value (single element) or random
* values (two elements representing the bound values)
* The latency simulates the delay from the time framework requesting HAL to authetication to
* the time when HAL is ready to perform authentication operations.
*
* This method fails with STATUS_INVALID_PARAMETERS if the passed-in array falls in any of
* the following conditions
* 1. the array contains no element
* 2. the array contains more than two elements
* 3. the array contains any negative value
* The accompanying error message gives more detail
*
* @param latencyMs[] value(s) are in milli-seconds
*/
void setOperationAuthenticateLatency(in int[] latencyMs);
/**
* setOperationAuthenticateDuration
*
* Set authentication duration covering the HAL authetication from start to end, including
* face capturing, and matching, acquired info reporting. In case a sequence of acquired
* info code are specified via setOperationAuthenticateAcquired(), the reporting is evenly
* distributed over the duration.
*
* This method fails with STATUS_INVALID_PARAMETERS if the passed-in value is negative
*
* @param duration value is in milli-seconds
*/
void setOperationAuthenticateDuration(in int durationMs);
/**
* setOperationAuthenticateError
*
* Force authentication to error out for non-zero error
* Check
* hardware/interfaces/biometrics/face/aidl/default/aidl/android/hardware/biometrics/face/Error.aidl
* for valid error codes
*
* @param error if error < 1000
* non-vendor error
* else
* vendor error
*/
void setOperationAuthenticateError(in int error);
/**
* setOperationAuthenticateAcquired
*
* Set one of more acquired info codes for the virtual hal to report during authentication
* Check
* hardware/interfaces/biometrics/face/aidl/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl
* for valid acquired info codes
*
* @param acquired[], one or more acquired info codes
*/
void setOperationAuthenticateAcquired(in AcquiredInfoAndVendorCode[] acquired);
/**
* setOperationEnrollLatency
*
* Set enrollment latency in the virtual hal in a fixed value (single element) or random
* values (two elements representing the bound values)
* The latency simulates the delay from the time framework requesting HAL to enroll to the
* time when HAL is ready to perform enrollment operations.
*
* This method fails with STATUS_INVALID_PARAMETERS if the passed-in array falls in any of
* the following conditions
* 1. the array contains no element
* 2. the array contains more than two elements
* 3. the array contains any negative value
* The accompanying error message gives more detail
*
* @param latencyMs[] value(s) are in milli-seconds
*/
void setOperationEnrollLatency(in int[] latencyMs);
/**
* setOperationDetectInteractionLatency
*
* Set detect interaction latency in the virtual hal in a fixed value (single element) or random
* values (two elements representing the bound values)
* The latency simulates the delay from the time framework requesting HAL to detect interaction
* to the time when HAL is ready to perform detect interaction operations.
*
* This method fails with STATUS_INVALID_PARAMETERS if the passed-in array falls in any of
* the following conditions
* 1. the array contains no element
* 2. the array contains more than two elements
* 3. the array contains any negative value
* The accompanying error message gives more detail
*
* @param latencyMs[] value(s) are in milli-seconds
*/
void setOperationDetectInteractionLatency(in int[] latencyMs);
/**
* setOperationDetectInteractionFails
*
* Force detect interaction operation to fail
*/
void setOperationDetectInteractionFails(in boolean error);
/**
* setLockout
*
* Whether to force to lockout on authentcation operation. If true, the virtual hal will report
* permanent lockout in processing authentication requrest, regardless of whether
* setLockoutEnable(true) is called or not.
*
* @param lockout, set to true if lockout is desired
*/
void setLockout(in boolean lockout);
/**
* setLockoutEnable
*
* Whether to enable authentication-fail-based lockout tracking or not. The lock tracking
* includes both timed-based (aka temporary) lockout and permanent lockout.
*
* @param enable, set true to enable the lockout tracking
*/
void setLockoutEnable(in boolean enable);
/**
* setLockoutTimedEnable
*
* Whether to enable authentication-fail-based time-based-lockout tracking or not.
*
* @param enable, set true to enable the time-basedlockout tracking
*/
void setLockoutTimedEnable(in boolean enable);
/**
* setLockoutTimedThreshold
*
* Set the number of consecutive authentication failures that triggers the timed-based lock to
* occur
*
* This method fails with STATUS_INVALID_PARAMETERS if the passed-in value is negative
*
* @param threshold, the number of consecutive failures
*/
void setLockoutTimedThreshold(in int threshold);
/**
* setLockoutTimedDuration
*
* Set the duration to expire timed-based lock during which there is no authentication failure
*
* This method fails with STATUS_INVALID_PARAMETERS if the passed-in value is negative
*
* @param duration, in milli-seconds
*/
void setLockoutTimedDuration(in int durationMs);
/**
* setLockoutPermanentThreshold
*
* Set the number of consecutive authentication failures that triggers the permanent lock to
* occur
*
* This method fails with STATUS_INVALID_PARAMETERS if the passed-in value is negative
*
* @param threshold, the number of consecutive failures
*/
void setLockoutPermanentThreshold(in int threshold);
/**
* resetConfigurations
*
* Reset all virtual hal configurations to default values
*/
void resetConfigurations();
/**
* setType
*
* Configure virtual face sensor type
*
* @param type, sensor type as specified in FaceSensorType.aidl
*
*/
void setType(in FaceSensorType type);
/**
* setSensorStrength
*
* Configure virtual face sensor strength
*
* @param sensor strength as specified in common/SensorStrength.aidl
*/
void setSensorStrength(in SensorStrength strength);
/**
* getFaceHal
*
* @return IFace interface associated with IVirtualHal instance
*/
IFace getFaceHal();
}

View File

@@ -14,12 +14,11 @@
* limitations under the License.
*/
package android.hardware.biometrics.fingerprint;
package android.hardware.biometrics.face.virtualhal;
/**
* @hide
*/
@VintfStability
parcelable NextEnrollment {
/**
* Identifier of the next enrollment if successful
@@ -31,7 +30,7 @@ parcelable NextEnrollment {
* and sequence of acquired info codes to be generated by HAL.
* See EnrollmentProgressStep.aidl for more details
*/
android.hardware.biometrics.fingerprint.EnrollmentProgressStep[] progressSteps;
android.hardware.biometrics.face.virtualhal.EnrollmentProgressStep[] progressSteps;
/**
* Success or failure of the next enrollment

View File

@@ -0,0 +1,3 @@
The aidl files in this directory are used to control/configure face virtual hal
via IVirtualHal interface

View File

@@ -9,21 +9,13 @@ package {
}
filegroup {
name: "face-example.rc",
srcs: ["face-example.rc"],
name: "face-virtual.rc",
srcs: ["face-virtual.rc"],
}
filegroup {
name: "face-example.xml",
srcs: ["face-example.xml"],
}
cc_binary {
name: "android.hardware.biometrics.face-service.example",
relative_install_path: "hw",
init_rc: [":face-example.rc"],
vintf_fragments: [":face-example.xml"],
vendor: true,
cc_library_static {
name: "android.hardware.biometrics.face-service.lib",
vendor_available: true,
shared_libs: [
"libbinder_ndk",
@@ -32,32 +24,80 @@ cc_binary {
],
srcs: [
"FakeLockoutTracker.cpp",
"main.cpp",
"Face.cpp",
"FakeFaceEngine.cpp",
"Session.cpp",
"FaceConfig.cpp",
"VirtualHal.cpp",
"main.cpp",
],
include_dirs: [
"frameworks/native/aidl/gui",
],
stl: "c++_static",
static_libs: [
whole_static_libs: [
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.biometrics.common.config",
"android.hardware.biometrics.common.thread",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.face.virtualhal-ndk",
"android.hardware.biometrics.face-V4-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.keymaster-V4-ndk",
"libandroid.hardware.biometrics.face.VirtualProps",
"libbase",
],
apex_available: [
"com.android.hardware.biometrics.face.virtual",
"//apex_available:platform",
],
}
cc_binary {
name: "android.hardware.biometrics.face-service.example",
system_ext_specific: true,
relative_install_path: "hw",
shared_libs: [
"libbinder_ndk",
"liblog",
"libnativewindow",
],
whole_static_libs: [
"android.hardware.biometrics.face-service.lib",
],
installable: false, // install APEX instead
apex_available: [
"com.android.hardware.biometrics.face.virtual",
"//apex_available:platform",
],
}
cc_binary {
name: "android.hardware.biometrics.face-service.default",
vendor: true,
relative_install_path: "hw",
init_rc: ["face-default.rc"],
vintf_fragments: ["face-default.xml"],
shared_libs: [
"libbinder_ndk",
"liblog",
"libnativewindow",
],
whole_static_libs: [
"android.hardware.biometrics.face-service.lib",
],
}
sysprop_library {
name: "android.hardware.biometrics.face.VirtualProps",
srcs: ["face.sysprop"],
property_owner: "Vendor",
vendor: true,
property_owner: "Platform",
vendor_available: true,
apex_available: [
"//apex_available:platform",
"com.android.hardware.biometrics.face.virtual",
],
}
cc_test {
@@ -66,6 +106,7 @@ cc_test {
"tests/FakeFaceEngineTest.cpp",
"FakeFaceEngine.cpp",
"FakeLockoutTracker.cpp",
"FaceConfig.cpp",
],
shared_libs: [
"libbase",
@@ -81,6 +122,8 @@ cc_test {
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.keymaster-V4-ndk",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.common.config",
"android.hardware.biometrics.common.thread",
],
vendor: true,
test_suites: ["general-tests"],
@@ -92,6 +135,7 @@ cc_test {
srcs: [
"tests/FakeLockoutTrackerTest.cpp",
"FakeLockoutTracker.cpp",
"FaceConfig.cpp",
],
shared_libs: [
"libbase",
@@ -107,8 +151,45 @@ cc_test {
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.keymaster-V4-ndk",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.common.config",
"android.hardware.biometrics.common.thread",
],
vendor: true,
test_suites: ["general-tests"],
require_root: true,
}
cc_test {
name: "android.hardware.biometrics.face.VirtualHalTest",
srcs: [
"tests/VirtualHalTest.cpp",
"FakeLockoutTracker.cpp",
"Face.cpp",
"FakeFaceEngine.cpp",
"Session.cpp",
"VirtualHal.cpp",
"FaceConfig.cpp",
],
shared_libs: [
"libbase",
"libbinder_ndk",
"libnativewindow",
"liblog",
],
include_dirs: [
"frameworks/native/aidl/gui",
],
static_libs: [
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.biometrics.common.config",
"android.hardware.biometrics.common.thread",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.face-V4-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.keymaster-V4-ndk",
"libandroid.hardware.biometrics.face.VirtualProps",
"android.hardware.biometrics.face.virtualhal-ndk",
],
test_suites: ["general-tests"],
require_root: true,
}

View File

@@ -34,9 +34,7 @@ using namespace ::android::face::virt;
namespace aidl::android::hardware::biometrics::face {
const int kSensorId = 4;
const common::SensorStrength kSensorStrength = FakeFaceEngine::GetSensorStrength();
const int kMaxEnrollmentsPerUser = 5;
const FaceSensorType kSensorType = FakeFaceEngine::GetSensorType();
const bool kHalControlsPreview = true;
const std::string kHwComponentId = "faceSensor";
const std::string kHardwareVersion = "vendor/model/revision";
@@ -62,13 +60,13 @@ ndk::ScopedAStatus Face::getSensorProps(std::vector<SensorProps>* return_val) {
common::CommonProps commonProps;
commonProps.sensorId = kSensorId;
commonProps.sensorStrength = kSensorStrength;
commonProps.sensorStrength = FakeFaceEngine::GetSensorStrength();
commonProps.maxEnrollmentsPerUser = kMaxEnrollmentsPerUser;
commonProps.componentInfo = {std::move(hw_component_info), std::move(sw_component_info)};
SensorProps props;
props.commonProps = std::move(commonProps);
props.sensorType = kSensorType;
props.sensorType = FakeFaceEngine::GetSensorType();
props.halControlsPreview = kHalControlsPreview;
props.enrollPreviewWidth = 1080;
props.enrollPreviewHeight = 1920;
@@ -141,6 +139,30 @@ binder_status_t Face::handleShellCommand(int in, int out, int err, const char**
return STATUS_OK;
}
const char* Face::type2String(FaceSensorType type) {
switch (type) {
case FaceSensorType::RGB:
return "rgb";
case FaceSensorType::IR:
return "ir";
default:
return "unknown";
}
}
const char* Face::strength2String(common::SensorStrength strength) {
switch (strength) {
case common::SensorStrength::STRONG:
return "STRONG";
case common::SensorStrength::WEAK:
return "WEAK";
case common::SensorStrength::CONVENIENCE:
return "CONVENIENCE";
default:
return "unknown";
}
}
void Face::onHelp(int fd) {
dprintf(fd, "Virtual Face HAL commands:\n");
dprintf(fd, " help: print this help\n");
@@ -167,7 +189,6 @@ void Face::resetConfigToDefault() {
RESET_CONFIG_O(lockout);
RESET_CONFIG_O(operation_authenticate_fails);
RESET_CONFIG_O(operation_detect_interaction_fails);
RESET_CONFIG_O(operation_enroll_fails);
RESET_CONFIG_V(operation_authenticate_latency);
RESET_CONFIG_V(operation_detect_interaction_latency);
RESET_CONFIG_V(operation_enroll_latency);

View File

@@ -17,6 +17,7 @@
#pragma once
#include <aidl/android/hardware/biometrics/face/BnFace.h>
#include "FaceConfig.h"
#include "Session.h"
namespace aidl::android::hardware::biometrics::face {
@@ -33,9 +34,20 @@ class Face : public BnFace {
binder_status_t dump(int fd, const char** args, uint32_t numArgs);
binder_status_t handleShellCommand(int in, int out, int err, const char** argv, uint32_t argc);
static FaceConfig& cfg() {
static FaceConfig* cfg = nullptr;
if (cfg == nullptr) {
cfg = new FaceConfig();
cfg->init();
}
return *cfg;
}
void resetConfigToDefault();
static const char* type2String(FaceSensorType type);
static const char* strength2String(common::SensorStrength strength);
private:
std::shared_ptr<Session> mSession;
void resetConfigToDefault();
void onHelp(int);
};

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2024 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.
*/
#define LOG_TAG "FaceConfig"
#include "FaceConfig.h"
#include <android-base/logging.h>
#include <face.sysprop.h>
using namespace ::android::face::virt;
namespace aidl::android::hardware::biometrics::face {
// Wrapper to system property access functions
#define CREATE_GETTER_SETTER_WRAPPER(_NAME_, _T_) \
ConfigValue _NAME_##Getter() { \
return FaceHalProperties::_NAME_(); \
} \
bool _NAME_##Setter(const ConfigValue& v) { \
return FaceHalProperties::_NAME_(std::get<_T_>(v)); \
}
CREATE_GETTER_SETTER_WRAPPER(type, OptString)
CREATE_GETTER_SETTER_WRAPPER(enrollments, OptIntVec)
CREATE_GETTER_SETTER_WRAPPER(enrollment_hit, OptInt32)
CREATE_GETTER_SETTER_WRAPPER(next_enrollment, OptString)
CREATE_GETTER_SETTER_WRAPPER(authenticator_id, OptInt64)
CREATE_GETTER_SETTER_WRAPPER(challenge, OptInt64)
CREATE_GETTER_SETTER_WRAPPER(strength, OptString)
CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_fails, OptBool)
CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_latency, OptIntVec)
CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_duration, OptInt32)
CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_error, OptInt32)
CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_acquired, OptString)
CREATE_GETTER_SETTER_WRAPPER(operation_enroll_latency, OptIntVec)
CREATE_GETTER_SETTER_WRAPPER(operation_detect_interaction_fails, OptBool)
CREATE_GETTER_SETTER_WRAPPER(operation_detect_interaction_latency, OptIntVec)
CREATE_GETTER_SETTER_WRAPPER(lockout, OptBool)
CREATE_GETTER_SETTER_WRAPPER(lockout_enable, OptBool)
CREATE_GETTER_SETTER_WRAPPER(lockout_timed_enable, OptBool)
CREATE_GETTER_SETTER_WRAPPER(lockout_timed_threshold, OptInt32)
CREATE_GETTER_SETTER_WRAPPER(lockout_timed_duration, OptInt32)
CREATE_GETTER_SETTER_WRAPPER(lockout_permanent_threshold, OptInt32)
CREATE_GETTER_SETTER_WRAPPER(features, OptIntVec)
// Name, Getter, Setter, Parser and default value
#define NGS(_NAME_) #_NAME_, _NAME_##Getter, _NAME_##Setter
static Config::Data configData[] = {
{NGS(type), &Config::parseString, "rgb"},
{NGS(enrollments), &Config::parseIntVec, ""},
{NGS(enrollment_hit), &Config::parseInt32, "0"},
{NGS(next_enrollment), &Config::parseString,
"1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true"},
{NGS(authenticator_id), &Config::parseInt64, "0"},
{NGS(challenge), &Config::parseInt64, ""},
{NGS(strength), &Config::parseString, "strong"},
{NGS(operation_authenticate_fails), &Config::parseBool, "false"},
{NGS(operation_authenticate_latency), &Config::parseIntVec, ""},
{NGS(operation_authenticate_duration), &Config::parseInt32, "500"},
{NGS(operation_authenticate_error), &Config::parseInt32, "0"},
{NGS(operation_authenticate_acquired), &Config::parseString, ""},
{NGS(operation_enroll_latency), &Config::parseIntVec, ""},
{NGS(operation_detect_interaction_latency), &Config::parseIntVec, ""},
{NGS(operation_detect_interaction_fails), &Config::parseBool, "false"},
{NGS(lockout), &Config::parseBool, "false"},
{NGS(lockout_enable), &Config::parseBool, "false"},
{NGS(lockout_timed_enable), &Config::parseBool, "false"},
{NGS(lockout_timed_threshold), &Config::parseInt32, "3"},
{NGS(lockout_timed_duration), &Config::parseInt32, "10000"},
{NGS(lockout_permanent_threshold), &Config::parseInt32, "5"},
{NGS(features), &Config::parseIntVec, ""}};
Config::Data* FaceConfig::getConfigData(int* size) {
*size = sizeof(configData) / sizeof(configData[0]);
return configData;
}
} // namespace aidl::android::hardware::biometrics::face

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2024 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 "config/Config.h"
namespace aidl::android::hardware::biometrics::face {
class FaceConfig : public Config {
Config::Data* getConfigData(int* size) override;
};
} // namespace aidl::android::hardware::biometrics::face

View File

@@ -23,6 +23,7 @@
#include <face.sysprop.h>
#include "Face.h"
#include "util/CancellationSignal.h"
#include "util/Util.h"
@@ -31,23 +32,23 @@ using namespace ::android::face::virt;
namespace aidl::android::hardware::biometrics::face {
FaceSensorType FakeFaceEngine::GetSensorType() {
std::string type = FaceHalProperties::type().value_or("");
std::string type = Face::cfg().get<std::string>("type");
if (type == "IR") {
return FaceSensorType::IR;
} else {
FaceHalProperties::type("RGB");
Face::cfg().set<std::string>("type", "RGB");
return FaceSensorType::RGB;
}
}
common::SensorStrength FakeFaceEngine::GetSensorStrength() {
std::string strength = FaceHalProperties::strength().value_or("");
std::string strength = Face::cfg().get<std::string>("strength");
if (strength == "convenience") {
return common::SensorStrength::CONVENIENCE;
} else if (strength == "weak") {
return common::SensorStrength::WEAK;
} else {
FaceHalProperties::strength("strong");
// Face::cfg().set<std::string>("strength", "strong");
return common::SensorStrength::STRONG;
}
}
@@ -56,13 +57,13 @@ void FakeFaceEngine::generateChallengeImpl(ISessionCallback* cb) {
BEGIN_OP(0);
std::uniform_int_distribution<int64_t> dist;
auto challenge = dist(mRandom);
FaceHalProperties::challenge(challenge);
Face::cfg().set<int64_t>("challenge", challenge);
cb->onChallengeGenerated(challenge);
}
void FakeFaceEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
BEGIN_OP(0);
FaceHalProperties::challenge({});
Face::cfg().set<int64_t>("challenge", 0);
cb->onChallengeRevoked(challenge);
}
void FakeFaceEngine::getEnrollmentConfigImpl(ISessionCallback* /*cb*/,
@@ -71,7 +72,7 @@ void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareA
EnrollmentType /*enrollmentType*/,
const std::vector<Feature>& /*features*/,
const std::future<void>& cancel) {
BEGIN_OP(getLatency(FaceHalProperties::operation_enroll_latency()));
BEGIN_OP(getLatency(Face::cfg().getopt<OptIntVec>("operation_enroll_latency")));
// Do proper HAT verification in the real implementation.
if (hat.mac.empty()) {
@@ -80,18 +81,19 @@ void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareA
return;
}
// Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
// ------:-----------------------------------------:--------------
// | | |--->enrollment success (true/false)
// | |--> progress_steps
// Format:
// <id>:<progress_ms-[acquiredInfo,...],...:<success>
// -------:--------------------------------------------------:--------------
// | | |--->enrollment
// success (true/false) | |--> progress_steps
// |
// |-->enrollment id
//
//
// progress_steps
// progress_steps:
// <progress_duration>-[acquiredInfo,...]+
// ---------------------------- ---------------------
// | |-> sequence of acquiredInfo code
// | |-> sequence of acquiredInfo code
// | --> time duration of the step in ms
//
// E.g. 1:2000-[21,1108,5,6,1],1000-[1113,4,1]:true
@@ -101,7 +103,7 @@ void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareA
//
std::string defaultNextEnrollment =
"1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true";
auto nextEnroll = FaceHalProperties::next_enrollment().value_or(defaultNextEnrollment);
auto nextEnroll = Face::cfg().get<std::string>("next_enrollment");
auto parts = Util::split(nextEnroll, ":");
if (parts.size() != 3) {
LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll;
@@ -137,19 +139,19 @@ void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareA
if (left == 0 && !IS_TRUE(parts[2])) { // end and failed
LOG(ERROR) << "Fail: requested by caller: " << nextEnroll;
FaceHalProperties::next_enrollment({});
Face::cfg().setopt<OptString>("next_enrollment", std::nullopt);
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
} else { // progress and update props if last time
LOG(INFO) << "onEnroll: " << enrollmentId << " left: " << left;
if (left == 0) {
auto enrollments = FaceHalProperties::enrollments();
auto enrollments = Face::cfg().getopt<OptIntVec>("enrollments");
enrollments.emplace_back(enrollmentId);
FaceHalProperties::enrollments(enrollments);
FaceHalProperties::next_enrollment({});
Face::cfg().setopt<OptIntVec>("enrollments", enrollments);
Face::cfg().setopt<OptString>("next_enrollment", std::nullopt);
// change authenticatorId after new enrollment
auto id = FaceHalProperties::authenticator_id().value_or(0);
auto id = Face::cfg().get<std::int64_t>("authenticator_id");
auto newId = id + 1;
FaceHalProperties::authenticator_id(newId);
Face::cfg().set<std::int64_t>("authenticator_id", newId);
LOG(INFO) << "Enrolled: " << enrollmentId;
}
cb->onEnrollmentProgress(enrollmentId, left);
@@ -159,10 +161,12 @@ void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareA
void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationId*/,
const std::future<void>& cancel) {
BEGIN_OP(getLatency(FaceHalProperties::operation_authenticate_latency()));
BEGIN_OP(getLatency(Face::cfg().getopt<OptIntVec>("operation_authenticate_latency")));
auto id = FaceHalProperties::enrollment_hit().value_or(0);
auto enrolls = FaceHalProperties::enrollments();
// SLEEP_MS(3000); //emulate hw HAL
auto id = Face::cfg().get<std::int32_t>("enrollment_hit");
auto enrolls = Face::cfg().getopt<OptIntVec>("enrollments");
auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
auto vec2str = [](std::vector<AcquiredInfo> va) {
@@ -192,10 +196,12 @@ void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationI
}
int64_t now = Util::getSystemNanoTime();
int64_t duration =
FaceHalProperties::operation_authenticate_duration().value_or(defaultAuthDuration);
auto acquired =
FaceHalProperties::operation_authenticate_acquired().value_or(defaultAcquiredInfo);
int64_t duration = Face::cfg().get<std::int32_t>("operation_authenticate_duration");
auto acquired = Face::cfg().get<std::string>("operation_authenticate_acquired");
if (acquired.empty()) {
Face::cfg().set<std::string>("operation_authenticate_acquired", defaultAcquiredInfo);
acquired = defaultAcquiredInfo;
}
auto acquiredInfos = Util::parseIntSequence(acquired);
int N = acquiredInfos.size();
@@ -211,21 +217,21 @@ void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationI
int i = 0;
do {
if (FaceHalProperties::lockout().value_or(false)) {
if (Face::cfg().get<bool>("lockout")) {
LOG(ERROR) << "Fail: lockout";
cb->onLockoutPermanent();
cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
return;
}
if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
if (Face::cfg().get<bool>("operation_authenticate_fails")) {
LOG(ERROR) << "Fail: operation_authenticate_fails";
mLockoutTracker.addFailedAttempt(cb);
cb->onAuthenticationFailed();
return;
}
auto err = FaceHalProperties::operation_authenticate_error().value_or(0);
auto err = Face::cfg().get<std::int32_t>("operation_authenticate_error");
if (err != 0) {
LOG(ERROR) << "Fail: operation_authenticate_error";
auto ec = convertError(err);
@@ -249,6 +255,15 @@ void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationI
LOG(INFO) << "AcquiredInfo:" << i << ": (" << (int)ac.first << "," << (int)ac.second
<< ")";
i++;
// the captured face id may change during authentication period
auto idnew = Face::cfg().get<std::int32_t>("enrollment_hit");
if (id != idnew) {
isEnrolled = std::find(enrolls.begin(), enrolls.end(), idnew) != enrolls.end();
LOG(INFO) << "enrollment_hit changed from " << id << " to " << idnew;
id = idnew;
break;
}
}
SLEEP_MS(duration / N);
@@ -292,9 +307,9 @@ std::pair<Error, int32_t> FakeFaceEngine::convertError(int32_t code) {
}
void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel) {
BEGIN_OP(getLatency(FaceHalProperties::operation_detect_interaction_latency()));
BEGIN_OP(getLatency(Face::cfg().getopt<OptIntVec>("operation_detect_interaction_latency")));
if (FaceHalProperties::operation_detect_interaction_fails().value_or(false)) {
if (Face::cfg().get<bool>("operation_detect_interaction_fails")) {
LOG(ERROR) << "Fail: operation_detect_interaction_fails";
cb->onError(Error::VENDOR, 0 /* vendorError */);
return;
@@ -306,8 +321,8 @@ void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::futu
return;
}
auto id = FaceHalProperties::enrollment_hit().value_or(0);
auto enrolls = FaceHalProperties::enrollments();
auto id = Face::cfg().get<std::int32_t>("enrollment_hit");
auto enrolls = Face::cfg().getopt<OptIntVec>("enrollments");
auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
if (id <= 0 || !isEnrolled) {
LOG(ERROR) << "Fail: not enrolled";
@@ -321,7 +336,7 @@ void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::futu
void FakeFaceEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) {
BEGIN_OP(0);
std::vector<int32_t> enrollments;
for (const auto& enrollmentId : FaceHalProperties::enrollments()) {
for (const auto& enrollmentId : Face::cfg().getopt<OptIntVec>("enrollments")) {
if (enrollmentId) {
enrollments.push_back(*enrollmentId);
}
@@ -334,20 +349,20 @@ void FakeFaceEngine::removeEnrollmentsImpl(ISessionCallback* cb,
BEGIN_OP(0);
std::vector<std::optional<int32_t>> newEnrollments;
for (const auto& enrollment : FaceHalProperties::enrollments()) {
for (const auto& enrollment : Face::cfg().getopt<OptIntVec>("enrollments")) {
auto id = enrollment.value_or(0);
if (std::find(enrollmentIds.begin(), enrollmentIds.end(), id) == enrollmentIds.end()) {
newEnrollments.emplace_back(id);
}
}
FaceHalProperties::enrollments(newEnrollments);
Face::cfg().setopt<OptIntVec>("enrollments", newEnrollments);
cb->onEnrollmentsRemoved(enrollmentIds);
}
void FakeFaceEngine::getFeaturesImpl(ISessionCallback* cb) {
BEGIN_OP(0);
if (FaceHalProperties::enrollments().empty()) {
if (Face::cfg().getopt<OptIntVec>("enrollments").empty()) {
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
return;
}
@@ -365,7 +380,7 @@ void FakeFaceEngine::setFeatureImpl(ISessionCallback* cb, const keymaster::Hardw
Feature feature, bool enabled) {
BEGIN_OP(0);
if (FaceHalProperties::enrollments().empty()) {
if (Face::cfg().getopt<OptIntVec>("enrollments").empty()) {
LOG(ERROR) << "Unable to set feature, enrollments are empty";
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
return;
@@ -377,7 +392,7 @@ void FakeFaceEngine::setFeatureImpl(ISessionCallback* cb, const keymaster::Hardw
return;
}
auto features = FaceHalProperties::features();
auto features = Face::cfg().getopt<OptIntVec>("features");
auto itr = std::find_if(features.begin(), features.end(), [feature](const auto& theFeature) {
return *theFeature == (int)feature;
@@ -389,7 +404,7 @@ void FakeFaceEngine::setFeatureImpl(ISessionCallback* cb, const keymaster::Hardw
features.push_back((int)feature);
}
FaceHalProperties::features(features);
Face::cfg().setopt<OptIntVec>("features", features);
cb->onFeatureSet(feature);
}
@@ -399,22 +414,22 @@ void FakeFaceEngine::getAuthenticatorIdImpl(ISessionCallback* cb) {
if (GetSensorStrength() != common::SensorStrength::STRONG) {
cb->onAuthenticatorIdRetrieved(0);
} else {
cb->onAuthenticatorIdRetrieved(FaceHalProperties::authenticator_id().value_or(0));
cb->onAuthenticatorIdRetrieved(Face::cfg().get<std::int64_t>("authenticator_id"));
}
}
void FakeFaceEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
BEGIN_OP(0);
int64_t authenticatorId = FaceHalProperties::authenticator_id().value_or(0);
int64_t authenticatorId = Face::cfg().get<std::int64_t>("authenticator_id");
int64_t newId = authenticatorId + 1;
FaceHalProperties::authenticator_id(newId);
Face::cfg().set<std::int64_t>("authenticator_id", newId);
cb->onAuthenticatorIdInvalidated(newId);
}
void FakeFaceEngine::resetLockoutImpl(ISessionCallback* cb,
const keymaster::HardwareAuthToken& /*hat*/) {
BEGIN_OP(0);
FaceHalProperties::lockout(false);
Face::cfg().set<bool>("lockout", false);
mLockoutTracker.reset();
cb->onLockoutCleared();
}

View File

@@ -19,6 +19,7 @@
#include "FakeLockoutTracker.h"
#include <android-base/logging.h>
#include <face.sysprop.h>
#include "Face.h"
#include "util/Util.h"
using namespace ::android::face::virt;
@@ -36,15 +37,15 @@ void FakeLockoutTracker::reset(bool dueToTimerExpire) {
}
void FakeLockoutTracker::addFailedAttempt(ISessionCallback* cb) {
bool lockoutEnabled = FaceHalProperties::lockout_enable().value_or(false);
bool timedLockoutenabled = FaceHalProperties::lockout_timed_enable().value_or(false);
bool lockoutEnabled = Face::cfg().get<bool>("lockout_enable");
bool timedLockoutenabled = Face::cfg().get<bool>("lockout_timed_enable");
if (lockoutEnabled) {
mFailedCount++;
mTimedFailedCount++;
mLastFailedTime = Util::getSystemNanoTime();
int32_t lockoutTimedThreshold = FaceHalProperties::lockout_timed_threshold().value_or(3);
int32_t lockoutTimedThreshold = Face::cfg().get<std::int32_t>("lockout_timed_threshold");
int32_t lockoutPermanetThreshold =
FaceHalProperties::lockout_permanent_threshold().value_or(5);
Face::cfg().get<std::int32_t>("lockout_permanent_threshold");
if (mFailedCount >= lockoutPermanetThreshold) {
mCurrentMode = LockoutMode::kPermanent;
LOG(ERROR) << "FakeLockoutTracker: lockoutPermanent";
@@ -68,7 +69,7 @@ FakeLockoutTracker::LockoutMode FakeLockoutTracker::getMode() {
}
int32_t FakeLockoutTracker::getTimedLockoutDuration() {
return FaceHalProperties::lockout_timed_duration().value_or(10 * 1000);
return Face::cfg().get<std::int32_t>("lockout_timed_duration");
}
int64_t FakeLockoutTracker::getLockoutTimeLeft() {

View File

@@ -0,0 +1,275 @@
/*
* Copyright (C) 2024 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 <unordered_map>
#include "VirtualHal.h"
#include <android-base/logging.h>
#include "util/CancellationSignal.h"
#undef LOG_TAG
#define LOG_TAG "FaceVirtualHalAidl"
namespace aidl::android::hardware::biometrics::face {
using AcquiredInfoAndVendorCode = virtualhal::AcquiredInfoAndVendorCode;
using Tag = AcquiredInfoAndVendorCode::Tag;
::ndk::ScopedAStatus VirtualHal::setEnrollments(const std::vector<int32_t>& enrollments) {
Face::cfg().sourcedFromAidl();
Face::cfg().setopt<OptIntVec>("enrollments", intVec2OptIntVec(enrollments));
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setEnrollmentHit(int32_t enrollment_hit) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<std::int32_t>("enrollment_hit", enrollment_hit);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setNextEnrollment(
const ::aidl::android::hardware::biometrics::face::NextEnrollment& next_enrollment) {
Face::cfg().sourcedFromAidl();
std::ostringstream os;
os << next_enrollment.id << ":";
int stepSize = next_enrollment.progressSteps.size();
for (int i = 0; i < stepSize; i++) {
auto& step = next_enrollment.progressSteps[i];
os << step.durationMs;
int acSize = step.acquiredInfoAndVendorCodes.size();
for (int j = 0; j < acSize; j++) {
if (j == 0) os << "-[";
auto& acquiredInfoAndVendorCode = step.acquiredInfoAndVendorCodes[j];
if (acquiredInfoAndVendorCode.getTag() == AcquiredInfoAndVendorCode::vendorCode)
os << acquiredInfoAndVendorCode.get<Tag::vendorCode>();
else if (acquiredInfoAndVendorCode.getTag() == AcquiredInfoAndVendorCode::acquiredInfo)
os << (int)acquiredInfoAndVendorCode.get<Tag::acquiredInfo>();
else
LOG(FATAL) << "ERROR: wrong AcquiredInfoAndVendorCode union tag";
if (j == acSize - 1)
os << "]";
else
os << ",";
}
if (i == stepSize - 1)
os << ":";
else
os << ",";
}
os << (next_enrollment.result ? "true" : "false");
Face::cfg().set<std::string>("next_enrollment", os.str());
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setAuthenticatorId(int64_t in_id) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<int64_t>("authenticator_id", in_id);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setChallenge(int64_t in_challenge) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<int64_t>("challenge", in_challenge);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateFails(bool in_fail) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<bool>("operation_authenticate_fails", in_fail);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateLatency(
const std::vector<int32_t>& in_latency) {
ndk::ScopedAStatus status = sanityCheckLatency(in_latency);
if (!status.isOk()) {
return status;
}
Face::cfg().sourcedFromAidl();
Face::cfg().setopt<OptIntVec>("operation_authenticate_latency", intVec2OptIntVec(in_latency));
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateDuration(int32_t in_duration) {
if (in_duration < 0) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IVirtualHal::STATUS_INVALID_PARAMETER, "Error: duration can not be negative"));
}
Face::cfg().sourcedFromAidl();
Face::cfg().set<int32_t>("operation_authenticate_duration", in_duration);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateError(int32_t in_error) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<int32_t>("operation_authenticate_error", in_error);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateAcquired(
const std::vector<AcquiredInfoAndVendorCode>& in_acquired) {
Face::cfg().sourcedFromAidl();
Face::cfg().setopt<OptIntVec>("operation_authenticate_acquired",
acquiredInfoVec2OptIntVec(in_acquired));
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setOperationEnrollLatency(const std::vector<int32_t>& in_latency) {
ndk::ScopedAStatus status = sanityCheckLatency(in_latency);
if (!status.isOk()) {
return status;
}
Face::cfg().sourcedFromAidl();
Face::cfg().setopt<OptIntVec>("operation_enroll_latency", intVec2OptIntVec(in_latency));
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setOperationDetectInteractionLatency(
const std::vector<int32_t>& in_latency) {
ndk::ScopedAStatus status = sanityCheckLatency(in_latency);
if (!status.isOk()) {
return status;
}
Face::cfg().sourcedFromAidl();
Face::cfg().setopt<OptIntVec>("operation_detect_interact_latency",
intVec2OptIntVec(in_latency));
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setOperationDetectInteractionFails(bool in_fails) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<bool>("operation_detect_interaction_fails", in_fails);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setLockout(bool in_lockout) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<bool>("lockout", in_lockout);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setLockoutEnable(bool in_enable) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<bool>("lockout_enable", in_enable);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setLockoutTimedEnable(bool in_enable) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<bool>("lockout_timed_enable", in_enable);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setLockoutTimedThreshold(int32_t in_threshold) {
if (in_threshold < 0) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IVirtualHal::STATUS_INVALID_PARAMETER, "Error: threshold can not be negative"));
}
Face::cfg().sourcedFromAidl();
Face::cfg().set<int32_t>("lockout_timed_threshold", in_threshold);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setLockoutTimedDuration(int32_t in_duration) {
if (in_duration < 0) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IVirtualHal::STATUS_INVALID_PARAMETER, "Error: duration can not be negative"));
}
Face::cfg().sourcedFromAidl();
Face::cfg().set<int32_t>("lockout_timed_duration", in_duration);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setLockoutPermanentThreshold(int32_t in_threshold) {
if (in_threshold < 0) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IVirtualHal::STATUS_INVALID_PARAMETER, "Error: threshold can not be negative"));
}
Face::cfg().sourcedFromAidl();
Face::cfg().set<int32_t>("lockout_permanent_threshold", in_threshold);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::resetConfigurations() {
Face::cfg().sourcedFromAidl();
Face::cfg().init();
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setType(
::aidl::android::hardware::biometrics::face::FaceSensorType in_type) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<std::string>("type", Face::type2String(in_type));
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setSensorStrength(common::SensorStrength in_strength) {
Face::cfg().sourcedFromAidl();
Face::cfg().set<std::string>("strength", Face::strength2String(in_strength));
return ndk::ScopedAStatus::ok();
}
OptIntVec VirtualHal::intVec2OptIntVec(const std::vector<int32_t>& in_vec) {
OptIntVec optIntVec;
std::transform(in_vec.begin(), in_vec.end(), std::back_inserter(optIntVec),
[](int value) { return std::optional<int>(value); });
return optIntVec;
}
OptIntVec VirtualHal::acquiredInfoVec2OptIntVec(
const std::vector<AcquiredInfoAndVendorCode>& in_vec) {
OptIntVec optIntVec;
std::transform(in_vec.begin(), in_vec.end(), std::back_inserter(optIntVec),
[](AcquiredInfoAndVendorCode ac) {
int value;
if (ac.getTag() == AcquiredInfoAndVendorCode::acquiredInfo)
value = (int)ac.get<Tag::acquiredInfo>();
else if (ac.getTag() == AcquiredInfoAndVendorCode::vendorCode)
value = ac.get<Tag::vendorCode>();
else
LOG(FATAL) << "ERROR: wrong AcquiredInfoAndVendorCode tag";
return std::optional<int>(value);
});
return optIntVec;
}
::ndk::ScopedAStatus VirtualHal::sanityCheckLatency(const std::vector<int32_t>& in_latency) {
if (in_latency.size() == 0 || in_latency.size() > 2) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IVirtualHal::STATUS_INVALID_PARAMETER,
"Error: input input array must contain 1 or 2 elements"));
}
for (auto x : in_latency) {
if (x < 0) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IVirtualHal::STATUS_INVALID_PARAMETER,
"Error: input data must not be negative"));
}
}
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::getFaceHal(std::shared_ptr<IFace>* pFace) {
*pFace = mFp;
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::biometrics::face

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2024 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/biometrics/face/virtualhal/BnVirtualHal.h>
#include "Face.h"
namespace aidl::android::hardware::biometrics::face {
using namespace virtualhal;
class VirtualHal : public BnVirtualHal {
public:
VirtualHal(std::shared_ptr<Face> fp) : mFp(fp) {}
::ndk::ScopedAStatus setEnrollments(const std::vector<int32_t>& in_id) override;
::ndk::ScopedAStatus setEnrollmentHit(int32_t in_hit_id) override;
::ndk::ScopedAStatus setNextEnrollment(
const ::aidl::android::hardware::biometrics::face::NextEnrollment& in_next_enrollment)
override;
::ndk::ScopedAStatus setAuthenticatorId(int64_t in_id) override;
::ndk::ScopedAStatus setChallenge(int64_t in_challenge) override;
::ndk::ScopedAStatus setOperationAuthenticateFails(bool in_fail) override;
::ndk::ScopedAStatus setOperationAuthenticateLatency(
const std::vector<int32_t>& in_latency) override;
::ndk::ScopedAStatus setOperationAuthenticateDuration(int32_t in_duration) override;
::ndk::ScopedAStatus setOperationAuthenticateError(int32_t in_error) override;
::ndk::ScopedAStatus setOperationAuthenticateAcquired(
const std::vector<AcquiredInfoAndVendorCode>& in_acquired) override;
::ndk::ScopedAStatus setOperationEnrollLatency(const std::vector<int32_t>& in_latency) override;
::ndk::ScopedAStatus setOperationDetectInteractionLatency(
const std::vector<int32_t>& in_latency) override;
::ndk::ScopedAStatus setOperationDetectInteractionFails(bool in_fails) override;
::ndk::ScopedAStatus setLockout(bool in_lockout) override;
::ndk::ScopedAStatus setLockoutEnable(bool in_enable) override;
::ndk::ScopedAStatus setLockoutTimedEnable(bool in_enable) override;
::ndk::ScopedAStatus setLockoutTimedThreshold(int32_t in_threshold) override;
::ndk::ScopedAStatus setLockoutTimedDuration(int32_t in_duration) override;
::ndk::ScopedAStatus setLockoutPermanentThreshold(int32_t in_threshold) override;
::ndk::ScopedAStatus resetConfigurations() override;
::ndk::ScopedAStatus setType(
::aidl::android::hardware::biometrics::face::FaceSensorType in_type) override;
::ndk::ScopedAStatus setSensorStrength(common::SensorStrength in_strength) override;
::ndk::ScopedAStatus getFaceHal(std::shared_ptr<IFace>* _aidl_return);
private:
OptIntVec intVec2OptIntVec(const std::vector<int32_t>& intVec);
OptIntVec acquiredInfoVec2OptIntVec(const std::vector<AcquiredInfoAndVendorCode>& intVec);
::ndk::ScopedAStatus sanityCheckLatency(const std::vector<int32_t>& in_latency);
std::shared_ptr<Face> mFp;
};
} // namespace aidl::android::hardware::biometrics::face

View File

@@ -23,7 +23,7 @@ apex {
key: "com.android.hardware.key",
certificate: ":com.android.hardware.certificate",
updatable: false,
vendor: true,
system_ext_specific: true,
binaries: [
// hal
@@ -31,9 +31,7 @@ apex {
],
prebuilts: [
// init_rc
"face-example-apex.rc",
// vintf_fragment
"face-example-apex.xml",
"face-virtual-apex.rc",
],
overrides: [
@@ -42,21 +40,7 @@ apex {
}
prebuilt_etc {
name: "face-example-apex.rc",
src: ":gen-face-example-apex.rc",
installable: false,
}
genrule {
name: "gen-face-example-apex.rc",
srcs: [":face-example.rc"],
out: ["face-example-apex.rc"],
cmd: "sed -e 's@/vendor/bin/@/apex/com.android.hardware.biometrics.face.virtual/bin/@' $(in) > $(out)",
}
prebuilt_etc {
name: "face-example-apex.xml",
src: ":face-example.xml",
sub_dir: "vintf",
name: "face-virtual-apex.rc",
src: ":face-virtual.rc",
installable: false,
}

View File

@@ -0,0 +1,133 @@
props {
owner: Vendor
module: "android.face.virt.FaceHalProperties"
prop {
api_name: "authenticator_id"
type: Long
access: ReadWrite
prop_name: "vendor.face.virtual.authenticator_id"
}
prop {
api_name: "challenge"
type: Long
access: ReadWrite
prop_name: "vendor.face.virtual.challenge"
}
prop {
api_name: "enrollment_hit"
type: Integer
access: ReadWrite
prop_name: "vendor.face.virtual.enrollment_hit"
}
prop {
api_name: "enrollments"
type: IntegerList
access: ReadWrite
prop_name: "persist.vendor.face.virtual.enrollments"
}
prop {
api_name: "features"
type: IntegerList
access: ReadWrite
prop_name: "persist.vendor.face.virtual.features"
}
prop {
api_name: "lockout"
access: ReadWrite
prop_name: "vendor.face.virtual.lockout"
}
prop {
api_name: "lockout_enable"
access: ReadWrite
prop_name: "persist.vendor.face.virtual.lockout_enable"
}
prop {
api_name: "lockout_permanent_threshold"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.face.virtual.lockout_permanent_threshold"
}
prop {
api_name: "lockout_timed_duration"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.face.virtual.lockout_timed_duration"
}
prop {
api_name: "lockout_timed_enable"
access: ReadWrite
prop_name: "persist.vendor.face.virtual.lockout_timed_enable"
}
prop {
api_name: "lockout_timed_threshold"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.face.virtual.lockout_timed_threshold"
}
prop {
api_name: "next_enrollment"
type: String
access: ReadWrite
prop_name: "vendor.face.virtual.next_enrollment"
}
prop {
api_name: "operation_authenticate_acquired"
type: String
access: ReadWrite
prop_name: "vendor.face.virtual.operation_authenticate_acquired"
}
prop {
api_name: "operation_authenticate_duration"
type: Integer
access: ReadWrite
prop_name: "vendor.face.virtual.operation_authenticate_duration"
}
prop {
api_name: "operation_authenticate_error"
type: Integer
access: ReadWrite
prop_name: "vendor.face.virtual.operation_authenticate_error"
}
prop {
api_name: "operation_authenticate_fails"
access: ReadWrite
prop_name: "vendor.face.virtual.operation_authenticate_fails"
}
prop {
api_name: "operation_authenticate_latency"
type: IntegerList
access: ReadWrite
prop_name: "vendor.face.virtual.operation_authenticate_latency"
}
prop {
api_name: "operation_detect_interaction_fails"
access: ReadWrite
prop_name: "vendor.face.virtual.operation_detect_interaction_fails"
}
prop {
api_name: "operation_detect_interaction_latency"
type: IntegerList
access: ReadWrite
prop_name: "vendor.face.virtual.operation_detect_interaction_latency"
}
prop {
api_name: "operation_enroll_latency"
type: IntegerList
access: ReadWrite
prop_name: "vendor.face.virtual.operation_enroll_latency"
}
prop {
api_name: "strength"
type: String
access: ReadWrite
prop_name: "persist.vendor.face.virtual.strength"
enum_values: "convenience|weak|strong"
}
prop {
api_name: "type"
type: String
access: ReadWrite
prop_name: "persist.vendor.face.virtual.type"
enum_values: "IR|RGB"
}
}

View File

@@ -0,0 +1,8 @@
service vendor.face-default /vendor/bin/hw/android.hardware.biometrics.face-service.default default
class hal
user nobody
group nobody
interface aidl android.hardware.biometrics.face.IFace/default
oneshot
disabled

View File

@@ -2,6 +2,6 @@
<hal format="aidl">
<name>android.hardware.biometrics.face</name>
<version>4</version>
<fqname>IFace/virtual</fqname>
<fqname>IFace/default</fqname>
</hal>
</manifest>

View File

@@ -1,8 +0,0 @@
service vendor.face-example /vendor/bin/hw/android.hardware.biometrics.face-service.example
class hal
user nobody
group nobody
interface aidl android.hardware.biometrics.face.IFace/virtual
oneshot
disabled

View File

@@ -0,0 +1,8 @@
service face-virtual /apex/com.android.hardware.biometrics.face.virtual/bin/hw/android.hardware.biometrics.face-service.example virtual
class hal
user nobody
group nobody
interface aidl android.hardware.biometrics.face.virtualhal.IVirtualHal/virtual
oneshot
disabled

View File

@@ -7,7 +7,7 @@ owner: Vendor
prop {
prop_name: "persist.vendor.face.virtual.type"
type: String
scope: Internal
scope: Public
access: ReadWrite
enum_values: "IR|RGB"
api_name: "type"
@@ -17,7 +17,7 @@ prop {
prop {
prop_name: "persist.vendor.face.virtual.strength"
type: String
scope: Internal
scope: Public
access: ReadWrite
enum_values: "convenience|weak|strong"
api_name: "strength"
@@ -27,7 +27,7 @@ prop {
prop {
prop_name: "persist.vendor.face.virtual.enrollments"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "enrollments"
}
@@ -36,7 +36,7 @@ prop {
prop {
prop_name: "persist.vendor.face.virtual.features"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "features"
}
@@ -46,7 +46,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.enrollment_hit"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "enrollment_hit"
}
@@ -60,7 +60,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.next_enrollment"
type: String
scope: Internal
scope: Public
access: ReadWrite
api_name: "next_enrollment"
}
@@ -69,7 +69,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.authenticator_id"
type: Long
scope: Internal
scope: Public
access: ReadWrite
api_name: "authenticator_id"
}
@@ -78,7 +78,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.challenge"
type: Long
scope: Internal
scope: Public
access: ReadWrite
api_name: "challenge"
}
@@ -87,7 +87,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.lockout"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout"
}
@@ -96,7 +96,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.operation_authenticate_fails"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_fails"
}
@@ -105,27 +105,18 @@ prop {
prop {
prop_name: "vendor.face.virtual.operation_detect_interaction_fails"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_detect_interaction_fails"
}
# force all enroll operations to fail
prop {
prop_name: "vendor.face.virtual.operation_enroll_fails"
type: Boolean
scope: Internal
access: ReadWrite
api_name: "operation_enroll_fails"
}
# add a latency to authentication operations
# Note that this latency is the initial authentication latency that occurs before
# the HAL will send AcquiredInfo::START and AcquiredInfo::FIRST_FRAME_RECEIVED
prop {
prop_name: "vendor.face.virtual.operation_authenticate_latency"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_latency"
}
@@ -134,7 +125,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.operation_detect_interaction_latency"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_detect_interaction_latency"
}
@@ -143,7 +134,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.operation_enroll_latency"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_enroll_latency"
}
@@ -153,7 +144,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.operation_authenticate_duration"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_duration"
}
@@ -162,7 +153,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.operation_authenticate_error"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_error"
}
@@ -171,7 +162,7 @@ prop {
prop {
prop_name: "vendor.face.virtual.operation_authenticate_acquired"
type: String
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_acquired"
}
@@ -180,7 +171,7 @@ prop {
prop {
prop_name: "persist.vendor.face.virtual.lockout_enable"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_enable"
}
@@ -189,7 +180,7 @@ prop {
prop {
prop_name: "persist.vendor.face.virtual.lockout_timed_enable"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_timed_enable"
}
@@ -198,7 +189,7 @@ prop {
prop {
prop_name: "persist.vendor.face.virtual.lockout_timed_threshold"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_timed_threshold"
}
@@ -207,7 +198,7 @@ prop {
prop {
prop_name: "persist.vendor.face.virtual.lockout_timed_duration"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_timed_duration"
}
@@ -216,7 +207,7 @@ prop {
prop {
prop_name: "persist.vendor.face.virtual.lockout_permanent_threshold"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_permanent_threshold"
}

View File

@@ -14,25 +14,49 @@
* limitations under the License.
*/
#undef LOG_TAG
#define LOG_TAG "FaceVirtualHal"
#include "Face.h"
#include "VirtualHal.h"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
using aidl::android::hardware::biometrics::face::Face;
using aidl::android::hardware::biometrics::face::VirtualHal;
int main() {
LOG(INFO) << "Face HAL started";
int main(int argc, char** argv) {
if (argc < 2) {
LOG(ERROR) << "Missing argument -> exiting, Valid arguments:[default|virtual]";
return EXIT_FAILURE;
}
LOG(INFO) << "Face HAL started: " << argv[1];
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<Face> hal = ndk::SharedRefBase::make<Face>();
std::shared_ptr<VirtualHal> hal_vhal = ndk::SharedRefBase::make<VirtualHal>(hal);
const std::string instance = std::string(Face::descriptor) + "/virtual";
binder_status_t status =
AServiceManager_registerLazyService(hal->asBinder().get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
if (strcmp(argv[1], "default") == 0) {
const std::string instance = std::string(Face::descriptor) + "/default";
auto binder = hal->asBinder();
binder_status_t status =
AServiceManager_registerLazyService(binder.get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
LOG(INFO) << "started IFace/default";
} else if (strcmp(argv[1], "virtual") == 0) {
const std::string instance = std::string(VirtualHal::descriptor) + "/virtual";
auto binder = hal_vhal->asBinder();
binder_status_t status =
AServiceManager_registerLazyService(binder.get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
LOG(INFO) << "started IVirtualHal/virtual";
} else {
LOG(ERROR) << "Unexpected argument: " << argv[1];
return EXIT_FAILURE;
}
AServiceManager_forceLazyServicesPersist(true);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
return EXIT_FAILURE; // should not reach here
}

View File

@@ -21,6 +21,7 @@
#include <aidl/android/hardware/biometrics/face/BnSessionCallback.h>
#include <android-base/logging.h>
#include "Face.h"
#include "FakeFaceEngine.h"
#include "util/Util.h"
@@ -141,12 +142,12 @@ class FakeFaceEngineTest : public ::testing::Test {
}
void TearDown() override {
FaceHalProperties::enrollments({});
FaceHalProperties::challenge({});
FaceHalProperties::features({});
FaceHalProperties::authenticator_id({});
FaceHalProperties::strength("");
FaceHalProperties::operation_detect_interaction_latency({});
Face::cfg().setopt<OptIntVec>("enrollments", {});
Face::cfg().set<std::int64_t>("challenge", 0);
Face::cfg().setopt<OptIntVec>("features", {});
Face::cfg().set<std::int64_t>("authenticator_id", 0);
Face::cfg().set<std::string>("strength", "");
Face::cfg().setopt<OptIntVec>("operation_detect_interaction_latency", {});
}
FakeFaceEngine mEngine;
@@ -160,81 +161,83 @@ TEST_F(FakeFaceEngineTest, one_eq_one) {
TEST_F(FakeFaceEngineTest, GenerateChallenge) {
mEngine.generateChallengeImpl(mCallback.get());
ASSERT_EQ(FaceHalProperties::challenge().value(), mCallback->mLastChallenge);
ASSERT_EQ(Face::cfg().get<std::int64_t>("challenge"), mCallback->mLastChallenge);
}
TEST_F(FakeFaceEngineTest, RevokeChallenge) {
auto challenge = FaceHalProperties::challenge().value_or(10);
auto challenge = Face::cfg().get<std::int64_t>("challenge");
mEngine.revokeChallengeImpl(mCallback.get(), challenge);
ASSERT_FALSE(FaceHalProperties::challenge().has_value());
ASSERT_FALSE(Face::cfg().get<std::int64_t>("challenge"));
ASSERT_EQ(challenge, mCallback->mLastChallengeRevoked);
}
TEST_F(FakeFaceEngineTest, ResetLockout) {
FaceHalProperties::lockout(true);
Face::cfg().set<bool>("lockout", true);
mEngine.resetLockoutImpl(mCallback.get(), {});
ASSERT_FALSE(mCallback->mLockoutPermanent);
ASSERT_FALSE(FaceHalProperties::lockout().value_or(true));
ASSERT_FALSE(Face::cfg().get<bool>("lockout"));
}
TEST_F(FakeFaceEngineTest, AuthenticatorId) {
FaceHalProperties::authenticator_id(50);
Face::cfg().set<std::int64_t>("authenticator_id", 50);
mEngine.getAuthenticatorIdImpl(mCallback.get());
ASSERT_EQ(50, mCallback->mLastAuthenticatorId);
ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
}
TEST_F(FakeFaceEngineTest, GetAuthenticatorIdWeakReturnsZero) {
FaceHalProperties::strength("weak");
FaceHalProperties::authenticator_id(500);
Face::cfg().set<std::string>("strength", "weak");
Face::cfg().set<std::int64_t>("authenticator_id", 500);
mEngine.getAuthenticatorIdImpl(mCallback.get());
ASSERT_EQ(0, mCallback->mLastAuthenticatorId);
ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
}
TEST_F(FakeFaceEngineTest, AuthenticatorIdInvalidate) {
FaceHalProperties::authenticator_id(500);
Face::cfg().set<std::int64_t>("authenticator_id", 500);
mEngine.invalidateAuthenticatorIdImpl(mCallback.get());
ASSERT_NE(500, FaceHalProperties::authenticator_id().value());
ASSERT_NE(500, Face::cfg().get<std::int64_t>("authenticator_id"));
ASSERT_TRUE(mCallback->mAuthenticatorIdInvalidated);
}
TEST_F(FakeFaceEngineTest, Enroll) {
FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:true");
Face::cfg().set<std::string>("next_enrollment",
"1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:true");
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
mCancel.get_future());
ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
ASSERT_EQ(1, FaceHalProperties::enrollments().size());
ASSERT_EQ(1, FaceHalProperties::enrollments()[0].value());
ASSERT_FALSE(Face::cfg().getopt<OptString>("next_enrollment").has_value());
ASSERT_EQ(1, Face::cfg().getopt<OptIntVec>("enrollments").size());
ASSERT_EQ(1, Face::cfg().getopt<OptIntVec>("enrollments")[0].value());
ASSERT_EQ(1, mCallback->mLastEnrolled);
ASSERT_EQ(0, mCallback->mRemaining);
}
TEST_F(FakeFaceEngineTest, EnrollFails) {
FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:false");
Face::cfg().set<std::string>("next_enrollment",
"1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:false");
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
mCancel.get_future());
ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
ASSERT_EQ(0, FaceHalProperties::enrollments().size());
ASSERT_FALSE(Face::cfg().getopt<OptString>("next_enrollment").has_value());
ASSERT_EQ(0, Face::cfg().getopt<OptIntVec>("enrollments").size());
}
TEST_F(FakeFaceEngineTest, EnrollCancel) {
FaceHalProperties::next_enrollment("1:2000-[21,8,9],300:false");
Face::cfg().set<std::string>("next_enrollment", "1:2000-[21,8,9],300:false");
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
mCancel.set_value();
mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
mCancel.get_future());
ASSERT_EQ(Error::CANCELED, mCallback->mError);
ASSERT_EQ(-1, mCallback->mLastEnrolled);
ASSERT_EQ(0, FaceHalProperties::enrollments().size());
ASSERT_TRUE(FaceHalProperties::next_enrollment().has_value());
ASSERT_EQ(0, Face::cfg().getopt<OptIntVec>("enrollments").size());
ASSERT_FALSE(Face::cfg().get<std::string>("next_enrollment").empty());
}
TEST_F(FakeFaceEngineTest, Authenticate) {
FaceHalProperties::enrollments({100});
FaceHalProperties::enrollment_hit(100);
Face::cfg().setopt<OptIntVec>("enrollments", {100});
Face::cfg().set<std::int32_t>("enrollment_hit", 100);
mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
ASSERT_EQ(100, mCallback->mLastAuthenticated);
@@ -242,32 +245,32 @@ TEST_F(FakeFaceEngineTest, Authenticate) {
}
TEST_F(FakeFaceEngineTest, AuthenticateCancel) {
FaceHalProperties::enrollments({100});
FaceHalProperties::enrollment_hit(100);
Face::cfg().setopt<OptIntVec>("enrollments", {100});
Face::cfg().set<std::int32_t>("enrollment_hit", 100);
mCancel.set_value();
mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
ASSERT_EQ(Error::CANCELED, mCallback->mError);
}
TEST_F(FakeFaceEngineTest, AuthenticateFailedForUnEnrolled) {
FaceHalProperties::enrollments({3});
FaceHalProperties::enrollment_hit(100);
Face::cfg().setopt<OptIntVec>("enrollments", {3});
Face::cfg().set<std::int32_t>("enrollment_hit", 100);
mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
ASSERT_EQ(Error::TIMEOUT, mCallback->mError);
ASSERT_TRUE(mCallback->mAuthenticateFailed);
}
TEST_F(FakeFaceEngineTest, DetectInteraction) {
FaceHalProperties::enrollments({100});
FaceHalProperties::enrollment_hit(100);
Face::cfg().setopt<OptIntVec>("enrollments", {100});
Face::cfg().set<std::int32_t>("enrollment_hit", 100);
ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
}
TEST_F(FakeFaceEngineTest, DetectInteractionCancel) {
FaceHalProperties::enrollments({100});
FaceHalProperties::enrollment_hit(100);
Face::cfg().setopt<OptIntVec>("enrollments", {100});
Face::cfg().set<std::int32_t>("enrollment_hit", 100);
mCancel.set_value();
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
ASSERT_EQ(Error::CANCELED, mCallback->mError);
@@ -279,7 +282,7 @@ TEST_F(FakeFaceEngineTest, GetFeatureEmpty) {
}
TEST_F(FakeFaceEngineTest, SetFeature) {
FaceHalProperties::enrollments({1});
Face::cfg().setopt<OptIntVec>("enrollments", {1});
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
auto features = mCallback->mFeatures;
@@ -294,7 +297,7 @@ TEST_F(FakeFaceEngineTest, SetFeature) {
}
TEST_F(FakeFaceEngineTest, ToggleFeature) {
FaceHalProperties::enrollments({1});
Face::cfg().setopt<OptIntVec>("enrollments", {1});
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
mEngine.getFeaturesImpl(mCallback.get());
@@ -310,7 +313,7 @@ TEST_F(FakeFaceEngineTest, ToggleFeature) {
}
TEST_F(FakeFaceEngineTest, TurningOffNonExistentFeatureDoesNothing) {
FaceHalProperties::enrollments({1});
Face::cfg().setopt<OptIntVec>("enrollments", {1});
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, false);
mEngine.getFeaturesImpl(mCallback.get());
@@ -319,7 +322,7 @@ TEST_F(FakeFaceEngineTest, TurningOffNonExistentFeatureDoesNothing) {
}
TEST_F(FakeFaceEngineTest, SetMultipleFeatures) {
FaceHalProperties::enrollments({1});
Face::cfg().setopt<OptIntVec>("enrollments", {1});
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_DIVERSE_POSES, true);
@@ -335,7 +338,7 @@ TEST_F(FakeFaceEngineTest, SetMultipleFeatures) {
}
TEST_F(FakeFaceEngineTest, SetMultipleFeaturesAndTurnOffSome) {
FaceHalProperties::enrollments({1});
Face::cfg().setopt<OptIntVec>("enrollments", {1});
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_DIVERSE_POSES, true);
@@ -352,7 +355,7 @@ TEST_F(FakeFaceEngineTest, SetMultipleFeaturesAndTurnOffSome) {
}
TEST_F(FakeFaceEngineTest, Enumerate) {
FaceHalProperties::enrollments({120, 3});
Face::cfg().setopt<OptIntVec>("enrollments", {120, 3});
mEngine.enumerateEnrollmentsImpl(mCallback.get());
auto enrolls = mCallback->mLastEnrollmentsEnumerated;
ASSERT_FALSE(enrolls.empty());
@@ -361,7 +364,7 @@ TEST_F(FakeFaceEngineTest, Enumerate) {
}
TEST_F(FakeFaceEngineTest, RemoveEnrollments) {
FaceHalProperties::enrollments({120, 3, 100});
Face::cfg().setopt<OptIntVec>("enrollments", {120, 3, 100});
mEngine.removeEnrollmentsImpl(mCallback.get(), {120, 100});
mEngine.enumerateEnrollmentsImpl(mCallback.get());
auto enrolls = mCallback->mLastEnrollmentsEnumerated;
@@ -372,9 +375,9 @@ TEST_F(FakeFaceEngineTest, RemoveEnrollments) {
}
TEST_F(FakeFaceEngineTest, ResetLockoutWithAuth) {
FaceHalProperties::lockout(true);
FaceHalProperties::enrollments({33});
FaceHalProperties::enrollment_hit(33);
Face::cfg().set<bool>("lockout", true);
Face::cfg().setopt<OptIntVec>("enrollments", {33});
Face::cfg().set<std::int32_t>("enrollment_hit", 33);
auto cancelFuture = mCancel.get_future();
mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, cancelFuture);
@@ -382,28 +385,30 @@ TEST_F(FakeFaceEngineTest, ResetLockoutWithAuth) {
mEngine.resetLockoutImpl(mCallback.get(), {} /* hat */);
ASSERT_FALSE(mCallback->mLockoutPermanent);
FaceHalProperties::enrollment_hit(33);
Face::cfg().set<std::int32_t>("enrollment_hit", 33);
mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, cancelFuture);
ASSERT_EQ(33, mCallback->mLastAuthenticated);
ASSERT_FALSE(mCallback->mAuthenticateFailed);
}
TEST_F(FakeFaceEngineTest, LatencyDefault) {
FaceHalProperties::operation_detect_interaction_latency({});
ASSERT_EQ(DEFAULT_LATENCY,
mEngine.getLatency(FaceHalProperties::operation_detect_interaction_latency()));
Face::cfg().setopt<OptIntVec>("operation_detect_interaction_latency", {});
ASSERT_EQ(DEFAULT_LATENCY, mEngine.getLatency(Face::cfg().getopt<OptIntVec>(
"operation_detect_interaction_latency")));
}
TEST_F(FakeFaceEngineTest, LatencyFixed) {
FaceHalProperties::operation_detect_interaction_latency({10});
ASSERT_EQ(10, mEngine.getLatency(FaceHalProperties::operation_detect_interaction_latency()));
Face::cfg().setopt<OptIntVec>("operation_detect_interaction_latency", {10});
ASSERT_EQ(10, mEngine.getLatency(
Face::cfg().getopt<OptIntVec>("operation_detect_interaction_latency")));
}
TEST_F(FakeFaceEngineTest, LatencyRandom) {
FaceHalProperties::operation_detect_interaction_latency({1, 1000});
Face::cfg().setopt<OptIntVec>("operation_detect_interaction_latency", {1, 1000});
std::set<int32_t> latencySet;
for (int i = 0; i < 100; i++) {
auto x = mEngine.getLatency(FaceHalProperties::operation_detect_interaction_latency());
auto x = mEngine.getLatency(
Face::cfg().getopt<OptIntVec>("operation_detect_interaction_latency"));
ASSERT_TRUE(x >= 1 && x <= 1000);
latencySet.insert(x);
}

View File

@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include "Face.h"
#include "FakeLockoutTracker.h"
#include "util/Util.h"
@@ -103,19 +104,21 @@ class FakeLockoutTrackerTest : public ::testing::Test {
static constexpr int32_t LOCKOUT_TIMED_DURATION = 100;
void SetUp() override {
FaceHalProperties::lockout_timed_threshold(LOCKOUT_TIMED_THRESHOLD);
FaceHalProperties::lockout_timed_duration(LOCKOUT_TIMED_DURATION);
FaceHalProperties::lockout_permanent_threshold(LOCKOUT_PERMANENT_THRESHOLD);
Face::cfg().set<std::int32_t>("lockout_timed_threshold", LOCKOUT_TIMED_THRESHOLD);
Face::cfg().set<std::int32_t>("lockout_timed_duration", LOCKOUT_TIMED_DURATION);
Face::cfg().set<std::int32_t>("lockout_permanent_threshold", LOCKOUT_PERMANENT_THRESHOLD);
Face::cfg().set<bool>("lockout_enable", false);
Face::cfg().set<bool>("lockout", false);
mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
}
void TearDown() override {
// reset to default
FaceHalProperties::lockout_timed_threshold(5);
FaceHalProperties::lockout_timed_duration(20);
FaceHalProperties::lockout_permanent_threshold(10000);
FaceHalProperties::lockout_enable(false);
FaceHalProperties::lockout(false);
Face::cfg().set<std::int32_t>("lockout_timed_threshold", 5);
Face::cfg().set<std::int32_t>("lockout_timed_duration", 20);
Face::cfg().set<std::int32_t>("lockout_permanent_threshold", 10000);
Face::cfg().set<bool>("lockout_enable", false);
Face::cfg().set<bool>("lockout", false);
}
FakeLockoutTracker mLockoutTracker;
@@ -123,7 +126,7 @@ class FakeLockoutTrackerTest : public ::testing::Test {
};
TEST_F(FakeLockoutTrackerTest, addFailedAttemptDisable) {
FaceHalProperties::lockout_enable(false);
Face::cfg().set<bool>("lockout_enable", false);
for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD + 1; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
@@ -131,7 +134,7 @@ TEST_F(FakeLockoutTrackerTest, addFailedAttemptDisable) {
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptPermanent) {
FaceHalProperties::lockout_enable(true);
Face::cfg().set<bool>("lockout_enable", true);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD - 1; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
@@ -145,8 +148,8 @@ TEST_F(FakeLockoutTrackerTest, addFailedAttemptPermanent) {
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimed) {
FaceHalProperties::lockout_enable(true);
FaceHalProperties::lockout_timed_enable(true);
Face::cfg().set<bool>("lockout_enable", true);
Face::cfg().set<bool>("lockout_timed_enable", true);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
@@ -168,8 +171,8 @@ TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimed) {
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockout_TimedThenPermanent) {
FaceHalProperties::lockout_enable(true);
FaceHalProperties::lockout_timed_enable(true);
Face::cfg().set<bool>("lockout_enable", true);
Face::cfg().set<bool>("lockout_timed_enable", true);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
@@ -182,8 +185,8 @@ TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockout_TimedThenPermanent) {
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimedTwice) {
FaceHalProperties::lockout_enable(true);
FaceHalProperties::lockout_timed_enable(true);
Face::cfg().set<bool>("lockout_enable", true);
Face::cfg().set<bool>("lockout_timed_enable", true);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
ASSERT_EQ(0, mCallback->mLockoutTimed);
for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
@@ -198,7 +201,7 @@ TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimedTwice) {
}
TEST_F(FakeLockoutTrackerTest, resetLockout) {
FaceHalProperties::lockout_enable(true);
Face::cfg().set<bool>("lockout_enable", true);
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());

View File

@@ -0,0 +1,237 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android/binder_process.h>
#include <face.sysprop.h>
#include <gtest/gtest.h>
#include <android-base/logging.h>
#include "Face.h"
#include "VirtualHal.h"
using namespace ::android::face::virt;
using namespace ::aidl::android::hardware::biometrics::face;
namespace aidl::android::hardware::biometrics::face {
class VirtualHalTest : public ::testing::Test {
public:
static const int32_t STATUS_FAILED_TO_SET_PARAMETER = 2;
protected:
void SetUp() override {
mHal = ndk::SharedRefBase::make<Face>();
mVhal = ndk::SharedRefBase::make<VirtualHal>(mHal);
ASSERT_TRUE(mVhal != nullptr);
mHal->resetConfigToDefault();
}
void TearDown() override { mHal->resetConfigToDefault(); }
std::shared_ptr<VirtualHal> mVhal;
ndk::ScopedAStatus validateNonNegativeInputOfInt32(const char* name,
ndk::ScopedAStatus (VirtualHal::*f)(int32_t),
const std::vector<int32_t>& in_good);
private:
std::shared_ptr<Face> mHal;
};
ndk::ScopedAStatus VirtualHalTest::validateNonNegativeInputOfInt32(
const char* name, ndk::ScopedAStatus (VirtualHal::*f)(int32_t),
const std::vector<int32_t>& in_params_good) {
ndk::ScopedAStatus status;
for (auto& param : in_params_good) {
status = (*mVhal.*f)(param);
if (!status.isOk()) return status;
if (Face::cfg().get<int32_t>(name) != param) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
VirtualHalTest::STATUS_FAILED_TO_SET_PARAMETER,
"Error: fail to set non-negative parameter"));
}
}
int32_t old_param = Face::cfg().get<int32_t>(name);
status = (*mVhal.*f)(-1);
if (status.isOk()) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
VirtualHalTest::STATUS_FAILED_TO_SET_PARAMETER, "Error: should return NOK"));
}
if (status.getServiceSpecificError() != IVirtualHal::STATUS_INVALID_PARAMETER) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
VirtualHalTest::STATUS_FAILED_TO_SET_PARAMETER,
"Error: unexpected return error code"));
}
if (Face::cfg().get<int32_t>(name) != old_param) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
VirtualHalTest::STATUS_FAILED_TO_SET_PARAMETER,
"Error: unexpected parameter change on failed attempt"));
}
return ndk::ScopedAStatus::ok();
}
TEST_F(VirtualHalTest, init) {
mVhal->setLockout(false);
ASSERT_TRUE(Face::cfg().get<bool>("lockout") == false);
ASSERT_TRUE(Face::cfg().get<std::string>("type") == "rgb");
ASSERT_TRUE(Face::cfg().get<std::string>("strength") == "strong");
std::int64_t id = Face::cfg().get<std::int64_t>("authenticator_id");
ASSERT_TRUE(Face::cfg().get<std::int64_t>("authenticator_id") == 0);
ASSERT_TRUE(Face::cfg().getopt<OptIntVec>("enrollments") == OptIntVec());
}
TEST_F(VirtualHalTest, enrollment_hit_int32) {
mVhal->setEnrollmentHit(11);
ASSERT_TRUE(Face::cfg().get<int32_t>("enrollment_hit") == 11);
}
TEST_F(VirtualHalTest, next_enrollment) {
struct {
std::string nextEnrollmentStr;
face::NextEnrollment nextEnrollment;
} testData[] = {
{"1:20:true", {1, {{20}}, true}},
{"1:50,60,70:true", {1, {{50}, {60}, {70}}, true}},
{"2:50-[21],60,70-[4,1002,1]:false",
{2,
{{50, {{AcquiredInfo::START}}},
{60},
{70, {{AcquiredInfo::TOO_DARK}, {1002}, {AcquiredInfo::GOOD}}}},
false}},
};
for (auto& d : testData) {
mVhal->setNextEnrollment(d.nextEnrollment);
ASSERT_TRUE(Face::cfg().get<std::string>("next_enrollment") == d.nextEnrollmentStr);
}
}
TEST_F(VirtualHalTest, authenticator_id_int64) {
mVhal->setAuthenticatorId(12345678900);
ASSERT_TRUE(Face::cfg().get<int64_t>("authenticator_id") == 12345678900);
}
TEST_F(VirtualHalTest, opeationAuthenticateFails_bool) {
mVhal->setOperationAuthenticateFails(true);
ASSERT_TRUE(Face::cfg().get<bool>("operation_authenticate_fails"));
}
TEST_F(VirtualHalTest, operationAuthenticateAcquired_int32_vector) {
using Tag = AcquiredInfoAndVendorCode::Tag;
std::vector<AcquiredInfoAndVendorCode> ac{
{AcquiredInfo::START}, {AcquiredInfo::TOO_FAR}, {1023}};
mVhal->setOperationAuthenticateAcquired(ac);
OptIntVec ac_get = Face::cfg().getopt<OptIntVec>("operation_authenticate_acquired");
ASSERT_TRUE(ac_get.size() == ac.size());
for (int i = 0; i < ac.size(); i++) {
int acCode = (ac[i].getTag() == Tag::acquiredInfo) ? (int)ac[i].get<Tag::acquiredInfo>()
: ac[i].get<Tag::vendorCode>();
ASSERT_TRUE(acCode == ac_get[i]);
}
}
TEST_F(VirtualHalTest, type) {
struct {
FaceSensorType type;
const char* typeStr;
} typeMap[] = {{FaceSensorType::RGB, "rgb"},
{FaceSensorType::IR, "ir"},
{FaceSensorType::UNKNOWN, "unknown"}};
for (auto const& x : typeMap) {
mVhal->setType(x.type);
ASSERT_TRUE(Face::cfg().get<std::string>("type") == x.typeStr);
}
}
TEST_F(VirtualHalTest, sensorStrength) {
struct {
common::SensorStrength strength;
const char* strengthStr;
} strengths[] = {{common::SensorStrength::CONVENIENCE, "CONVENIENCE"},
{common::SensorStrength::WEAK, "WEAK"},
{common::SensorStrength::STRONG, "STRONG"}};
for (auto const& x : strengths) {
mVhal->setSensorStrength(x.strength);
ASSERT_TRUE(Face::cfg().get<std::string>("strength") == x.strengthStr);
}
}
TEST_F(VirtualHalTest, setLatency) {
ndk::ScopedAStatus status;
std::vector<int32_t> in_lats[] = {{1}, {2, 3}, {5, 4}};
for (auto const& in_lat : in_lats) {
status = mVhal->setOperationAuthenticateLatency(in_lat);
ASSERT_TRUE(status.isOk());
OptIntVec out_lat = Face::cfg().getopt<OptIntVec>("operation_authenticate_latency");
ASSERT_TRUE(in_lat.size() == out_lat.size());
for (int i = 0; i < in_lat.size(); i++) {
ASSERT_TRUE(in_lat[i] == out_lat[i]);
}
}
std::vector<int32_t> bad_in_lats[] = {{}, {1, 2, 3}, {1, -3}};
for (auto const& in_lat : bad_in_lats) {
status = mVhal->setOperationAuthenticateLatency(in_lat);
ASSERT_TRUE(!status.isOk());
ASSERT_TRUE(status.getServiceSpecificError() == IVirtualHal::STATUS_INVALID_PARAMETER);
}
}
TEST_F(VirtualHalTest, setOperationAuthenticateDuration) {
ndk::ScopedAStatus status = validateNonNegativeInputOfInt32(
"operation_authenticate_duration", &IVirtualHal::setOperationAuthenticateDuration,
{0, 33});
ASSERT_TRUE(status.isOk());
}
TEST_F(VirtualHalTest, setLockoutTimedDuration) {
ndk::ScopedAStatus status = validateNonNegativeInputOfInt32(
"lockout_timed_duration", &IVirtualHal::setLockoutTimedDuration, {0, 35});
ASSERT_TRUE(status.isOk());
}
TEST_F(VirtualHalTest, setLockoutTimedThreshold) {
ndk::ScopedAStatus status = validateNonNegativeInputOfInt32(
"lockout_timed_threshold", &IVirtualHal::setLockoutTimedThreshold, {0, 36});
ASSERT_TRUE(status.isOk());
}
TEST_F(VirtualHalTest, setLockoutPermanentThreshold) {
ndk::ScopedAStatus status = validateNonNegativeInputOfInt32(
"lockout_permanent_threshold", &IVirtualHal::setLockoutPermanentThreshold, {0, 37});
ASSERT_TRUE(status.isOk());
}
TEST_F(VirtualHalTest, setOthers) {
// Verify that there is no CHECK() failures
mVhal->setEnrollments({7, 6, 5});
mVhal->setChallenge(111222333444555666);
mVhal->setOperationAuthenticateError(4);
mVhal->setOperationEnrollLatency({4, 5});
mVhal->setLockout(false);
mVhal->setLockoutEnable(false);
}
} // namespace aidl::android::hardware::biometrics::face
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}

View File

@@ -11,7 +11,7 @@ aidl_interface {
name: "android.hardware.biometrics.fingerprint",
vendor_available: true,
srcs: [
"android/hardware/biometrics/fingerprint/**/*.aidl",
"android/hardware/biometrics/fingerprint/*.aidl",
],
imports: [
"android.hardware.biometrics.common-V4",
@@ -25,6 +25,12 @@ aidl_interface {
cpp: {
enabled: false,
},
ndk: {
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
],
},
rust: {
enabled: true,
},
@@ -60,5 +66,34 @@ aidl_interface {
},
],
frozen: true,
}
aidl_interface {
name: "android.hardware.biometrics.fingerprint.virtualhal",
srcs: [
"android/hardware/biometrics/fingerprint/virtualhal/*.aidl",
],
imports: [
"android.hardware.biometrics.common-V4",
"android.hardware.keymaster-V4",
"android.hardware.biometrics.fingerprint-V4",
],
vendor_available: true,
unstable: true,
backend: {
java: {
platform_apis: true,
},
cpp: {
enabled: false,
},
ndk: {
apex_available: [
"com.android.hardware.biometrics.fingerprint.virtual",
"//apex_available:platform",
],
},
},
frozen: false,
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint;
/* @hide */
@VintfStability
interface IVirtualHal {
oneway void setEnrollments(in int[] id);
oneway void setEnrollmentHit(in int hit_id);
oneway void setNextEnrollment(in android.hardware.biometrics.fingerprint.NextEnrollment next_enrollment);
oneway void setAuthenticatorId(in long id);
oneway void setChallenge(in long challenge);
oneway void setOperationAuthenticateFails(in boolean fail);
oneway void setOperationAuthenticateLatency(in int[] latencyMs);
oneway void setOperationAuthenticateDuration(in int durationMs);
oneway void setOperationAuthenticateError(in int error);
oneway void setOperationAuthenticateAcquired(in android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode[] acquired);
oneway void setOperationEnrollError(in int error);
oneway void setOperationEnrollLatency(in int[] latencyMs);
oneway void setOperationDetectInteractionLatency(in int[] latencyMs);
oneway void setOperationDetectInteractionError(in int error);
oneway void setOperationDetectInteractionDuration(in int durationMs);
oneway void setOperationDetectInteractionAcquired(in android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode[] acquired);
oneway void setLockout(in boolean lockout);
oneway void setLockoutEnable(in boolean enable);
oneway void setLockoutTimedThreshold(in int threshold);
oneway void setLockoutTimedDuration(in int durationMs);
oneway void setLockoutPermanentThreshold(in int threshold);
oneway void resetConfigurations();
oneway void setType(in android.hardware.biometrics.fingerprint.FingerprintSensorType type);
oneway void setSensorId(in int id);
oneway void setSensorStrength(in android.hardware.biometrics.common.SensorStrength strength);
oneway void setMaxEnrollmentPerUser(in int max);
oneway void setSensorLocation(in android.hardware.biometrics.fingerprint.SensorLocation loc);
oneway void setNavigationGuesture(in boolean v);
oneway void setDetectInteraction(in boolean v);
oneway void setDisplayTouch(in boolean v);
oneway void setControlIllumination(in boolean v);
const int STATUS_INVALID_PARAMETER = 1;
}

View File

@@ -14,14 +14,13 @@
* limitations under the License.
*/
package android.hardware.biometrics.fingerprint;
package android.hardware.biometrics.fingerprint.virtualhal;
import android.hardware.biometrics.fingerprint.AcquiredInfo;
/**
* @hide
*/
@VintfStability
union AcquiredInfoAndVendorCode {
/**
* Acquired info as specified in AcqauiredInfo.aidl

View File

@@ -14,14 +14,13 @@
* limitations under the License.
*/
package android.hardware.biometrics.fingerprint;
package android.hardware.biometrics.fingerprint.virtualhal;
import android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode;
import android.hardware.biometrics.fingerprint.virtualhal.AcquiredInfoAndVendorCode;
/**
* @hide
*/
@VintfStability
parcelable EnrollmentProgressStep {
/**
* The duration of the enrollment step in milli-seconds

View File

@@ -14,19 +14,19 @@
* limitations under the License.
*/
package android.hardware.biometrics.fingerprint;
package android.hardware.biometrics.fingerprint.virtualhal;
import android.hardware.biometrics.common.SensorStrength;
import android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode;
import android.hardware.biometrics.fingerprint.FingerprintSensorType;
import android.hardware.biometrics.fingerprint.NextEnrollment;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorLocation;
import android.hardware.biometrics.fingerprint.virtualhal.AcquiredInfoAndVendorCode;
import android.hardware.biometrics.fingerprint.virtualhal.NextEnrollment;
/**
* @hide
*/
@VintfStability
oneway interface IVirtualHal {
interface IVirtualHal {
/**
* The operation failed due to invalid input parameters, the error messages should
* gives more details
@@ -315,4 +315,5 @@ oneway interface IVirtualHal {
void setDetectInteraction(in boolean v);
void setDisplayTouch(in boolean v);
void setControlIllumination(in boolean v);
IFingerprint getFingerprintHal();
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint.virtualhal;
import android.hardware.biometrics.fingerprint.virtualhal.EnrollmentProgressStep;
/**
* @hide
*/
parcelable NextEnrollment {
/**
* Identifier of the next enrollment if successful
*/
int id;
/**
* Specification of the progress steps of the next enrollment, each step consists of duration
* and sequence of acquired info codes to be generated by HAL.
* See EnrollmentProgressStep.aidl for more details
*/
EnrollmentProgressStep[] progressSteps;
/**
* Success or failure of the next enrollment
*/
boolean result = true;
}

View File

@@ -0,0 +1,2 @@
The aidl files in this directory are used only by fingerprint virtual hal
which is controlled/configured via IVirtualHal interface

View File

@@ -8,10 +8,9 @@ package {
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_binary {
name: "android.hardware.biometrics.fingerprint-service.example",
vendor: true,
relative_install_path: "hw",
cc_library_static {
name: "android.hardware.biometrics.fingerprint-service.lib",
vendor_available: true,
local_include_dirs: ["include"],
srcs: [
"FakeLockoutTracker.cpp",
@@ -30,22 +29,80 @@ cc_binary {
"libbinder_ndk",
"liblog",
],
static_libs: [
whole_static_libs: [
"libandroid.hardware.biometrics.fingerprint.VirtualProps",
"libbase",
"android.hardware.biometrics.fingerprint-V5-ndk",
"android.hardware.biometrics.fingerprint.virtualhal-ndk",
"android.hardware.biometrics.fingerprint-V4-ndk",
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.biometrics.common.thread",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.common.config",
"android.hardware.keymaster-V4-ndk",
],
product_variables: {
debuggable: {
cflags: ["-DFPS_DEBUGGABLE"],
},
},
apex_available: [
"com.android.hardware.biometrics.fingerprint.virtual",
"//apex_available:platform",
],
}
cc_binary {
name: "android.hardware.biometrics.fingerprint-service.example",
system_ext_specific: true,
relative_install_path: "hw",
local_include_dirs: ["include"],
srcs: [
],
stl: "c++_static",
shared_libs: [
"libbinder_ndk",
"liblog",
],
whole_static_libs: [
"android.hardware.biometrics.fingerprint-service.lib",
],
installable: false, // install APEX instead
product_variables: {
debuggable: {
cflags: ["-DFPS_DEBUGGABLE"],
},
},
apex_available: [
"com.android.hardware.biometrics.fingerprint.virtual",
],
}
cc_binary {
name: "android.hardware.biometrics.fingerprint-service.default",
//system_ext_specific: true,
vendor: true,
relative_install_path: "hw",
init_rc: ["fingerprint-default.rc"],
vintf_fragments: ["fingerprint-default.xml"],
local_include_dirs: ["include"],
srcs: [
],
stl: "c++_static",
shared_libs: [
"libbinder_ndk",
"liblog",
],
whole_static_libs: [
"android.hardware.biometrics.fingerprint-service.lib",
],
product_variables: {
debuggable: {
cflags: ["-DFPS_DEBUGGABLE"],
},
},
apex_available: [
"//apex_available:platform",
],
}
cc_test {
@@ -63,14 +120,13 @@ cc_test {
],
static_libs: [
"libandroid.hardware.biometrics.fingerprint.VirtualProps",
"android.hardware.biometrics.fingerprint-V5-ndk",
"android.hardware.biometrics.fingerprint-V4-ndk",
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.keymaster-V4-ndk",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.common.config",
"android.hardware.biometrics.common.thread",
],
vendor: true,
test_suites: ["general-tests"],
require_root: true,
}
@@ -91,14 +147,13 @@ cc_test {
],
static_libs: [
"libandroid.hardware.biometrics.fingerprint.VirtualProps",
"android.hardware.biometrics.fingerprint-V5-ndk",
"android.hardware.biometrics.fingerprint-V4-ndk",
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.keymaster-V4-ndk",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.common.config",
"android.hardware.biometrics.common.thread",
],
vendor: true,
test_suites: ["general-tests"],
require_root: true,
}
@@ -117,14 +172,13 @@ cc_test {
],
static_libs: [
"libandroid.hardware.biometrics.fingerprint.VirtualProps",
"android.hardware.biometrics.fingerprint-V5-ndk",
"android.hardware.biometrics.fingerprint-V4-ndk",
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.keymaster-V4-ndk",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.common.thread",
"android.hardware.biometrics.common.config",
],
vendor: true,
test_suites: ["general-tests"],
require_root: true,
}
@@ -145,14 +199,13 @@ cc_test {
],
static_libs: [
"libandroid.hardware.biometrics.fingerprint.VirtualProps",
"android.hardware.biometrics.fingerprint-V5-ndk",
"android.hardware.biometrics.fingerprint-V4-ndk",
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.keymaster-V4-ndk",
"android.hardware.biometrics.common.util",
"android.hardware.biometrics.common.thread",
"android.hardware.biometrics.common.config",
],
vendor: true,
test_suites: ["general-tests"],
require_root: true,
}
@@ -178,7 +231,8 @@ cc_test {
],
static_libs: [
"libandroid.hardware.biometrics.fingerprint.VirtualProps",
"android.hardware.biometrics.fingerprint-V5-ndk",
"android.hardware.biometrics.fingerprint-V4-ndk",
"android.hardware.biometrics.fingerprint.virtualhal-ndk",
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.keymaster-V4-ndk",
"android.hardware.biometrics.common.util",
@@ -190,7 +244,6 @@ cc_test {
cflags: ["-DFPS_DEBUGGABLE"],
},
},
vendor: true,
test_suites: ["general-tests"],
require_root: true,
}
@@ -198,39 +251,34 @@ cc_test {
sysprop_library {
name: "android.hardware.biometrics.fingerprint.VirtualProps",
srcs: ["fingerprint.sysprop"],
property_owner: "Vendor",
vendor: true,
property_owner: "Platform",
vendor_available: true,
apex_available: [
"com.android.hardware.biometrics.fingerprint.virtual",
"//apex_available:platform",
],
}
prebuilt_etc {
name: "fingerprint-example.rc",
src: "fingerprint-example.rc",
installable: false,
}
prebuilt_etc {
name: "fingerprint-example.xml",
src: "fingerprint-example.xml",
sub_dir: "vintf",
name: "fingerprint-virtual.rc",
src: "fingerprint-virtual.rc",
installable: false,
}
apex {
name: "com.android.hardware.biometrics.fingerprint.virtual",
manifest: "apex_manifest.json",
file_contexts: "apex_file_contexts",
file_contexts: ":com.android.biometrics.virtual.fingerprint-file_contexts",
key: "com.android.hardware.key",
certificate: ":com.android.hardware.certificate",
updatable: false,
vendor: true,
system_ext_specific: true,
binaries: [
"android.hardware.biometrics.fingerprint-service.example",
],
prebuilts: [
// init_rc
"fingerprint-example.rc",
// vintf_fragment
"fingerprint-example.xml",
"fingerprint-virtual.rc",
],
}

View File

@@ -389,10 +389,10 @@ void FakeFingerprintEngine::resetLockoutImpl(ISessionCallback* cb,
if (isLockoutTimerStarted) isLockoutTimerAborted = true;
}
void FakeFingerprintEngine::clearLockout(ISessionCallback* cb) {
void FakeFingerprintEngine::clearLockout(ISessionCallback* cb, bool dueToTimeout) {
Fingerprint::cfg().set<bool>("lockout", false);
cb->onLockoutCleared();
mLockoutTracker.reset();
mLockoutTracker.reset(dueToTimeout);
}
ndk::ScopedAStatus FakeFingerprintEngine::onPointerDownImpl(int32_t /*pointerId*/, int32_t /*x*/,
@@ -536,7 +536,7 @@ void FakeFingerprintEngine::startLockoutTimer(int64_t timeout, ISessionCallback*
void FakeFingerprintEngine::lockoutTimerExpired(ISessionCallback* cb) {
BEGIN_OP(0);
if (!isLockoutTimerAborted) {
clearLockout(cb);
clearLockout(cb, true);
}
isLockoutTimerStarted = false;
isLockoutTimerAborted = false;

View File

@@ -23,8 +23,11 @@ using namespace ::android::fingerprint::virt;
namespace aidl::android::hardware::biometrics::fingerprint {
void FakeLockoutTracker::reset() {
mFailedCount = 0;
void FakeLockoutTracker::reset(bool dueToTimeout) {
if (!dueToTimeout) {
mFailedCount = 0;
}
mFailedCountTimed = 0;
mLockoutTimedStart = 0;
mCurrentMode = LockoutMode::kNone;
}
@@ -33,6 +36,7 @@ void FakeLockoutTracker::addFailedAttempt() {
bool enabled = Fingerprint::cfg().get<bool>("lockout_enable");
if (enabled) {
mFailedCount++;
mFailedCountTimed++;
int32_t lockoutTimedThreshold =
Fingerprint::cfg().get<std::int32_t>("lockout_timed_threshold");
int32_t lockoutPermanetThreshold =
@@ -40,7 +44,7 @@ void FakeLockoutTracker::addFailedAttempt() {
if (mFailedCount >= lockoutPermanetThreshold) {
mCurrentMode = LockoutMode::kPermanent;
Fingerprint::cfg().set<bool>("lockout", true);
} else if (mFailedCount >= lockoutTimedThreshold) {
} else if (mFailedCountTimed >= lockoutTimedThreshold) {
if (mCurrentMode == LockoutMode::kNone) {
mCurrentMode = LockoutMode::kTimed;
mLockoutTimedStart = Util::getSystemNanoTime();

View File

@@ -26,7 +26,7 @@
#define LOG_TAG "FingerprintVirtualHalAidl"
namespace aidl::android::hardware::biometrics::fingerprint {
using AcquiredInfoAndVendorCode = virtualhal::AcquiredInfoAndVendorCode;
using Tag = AcquiredInfoAndVendorCode::Tag;
::ndk::ScopedAStatus VirtualHal::setEnrollments(const std::vector<int32_t>& enrollments) {
@@ -41,8 +41,7 @@ using Tag = AcquiredInfoAndVendorCode::Tag;
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::setNextEnrollment(
const ::aidl::android::hardware::biometrics::fingerprint::NextEnrollment& next_enrollment) {
::ndk::ScopedAStatus VirtualHal::setNextEnrollment(const NextEnrollment& next_enrollment) {
Fingerprint::cfg().sourcedFromAidl();
std::ostringstream os;
os << next_enrollment.id << ":";
@@ -333,4 +332,10 @@ OptIntVec VirtualHal::acquiredInfoVec2OptIntVec(
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualHal::getFingerprintHal(
std::shared_ptr<::aidl::android::hardware::biometrics::fingerprint::IFingerprint>* pFp) {
LOG(INFO) << " calling getFingerprintHal in VirtualHal.cpp";
*pFp = mFp;
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::biometrics::fingerprint

View File

@@ -0,0 +1,178 @@
props {
owner: Vendor
module: "android.fingerprint.virt.FingerprintHalProperties"
prop {
api_name: "authenticator_id"
type: Long
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
}
prop {
api_name: "challenge"
type: Long
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.challenge"
}
prop {
api_name: "control_illumination"
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination"
}
prop {
api_name: "detect_interaction"
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.detect_interaction"
}
prop {
api_name: "display_touch"
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch"
}
prop {
api_name: "enrollment_hit"
type: Integer
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.enrollment_hit"
}
prop {
api_name: "enrollments"
type: IntegerList
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.enrollments"
}
prop {
api_name: "lockout"
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.lockout"
}
prop {
api_name: "lockout_enable"
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.lockout_enable"
}
prop {
api_name: "lockout_permanent_threshold"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.lockout_permanent_threshold"
}
prop {
api_name: "lockout_timed_duration"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_duration"
}
prop {
api_name: "lockout_timed_threshold"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_threshold"
}
prop {
api_name: "max_enrollments"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.max_enrollments"
}
prop {
api_name: "navigation_guesture"
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture"
}
prop {
api_name: "next_enrollment"
type: String
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.next_enrollment"
}
prop {
api_name: "operation_authenticate_acquired"
type: String
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired"
}
prop {
api_name: "operation_authenticate_duration"
type: Integer
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
}
prop {
api_name: "operation_authenticate_error"
type: Integer
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_authenticate_error"
}
prop {
api_name: "operation_authenticate_fails"
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
}
prop {
api_name: "operation_authenticate_latency"
type: IntegerList
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
}
prop {
api_name: "operation_detect_interaction_acquired"
type: String
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired"
}
prop {
api_name: "operation_detect_interaction_duration"
type: Integer
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration"
}
prop {
api_name: "operation_detect_interaction_error"
type: Integer
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error"
}
prop {
api_name: "operation_detect_interaction_latency"
type: IntegerList
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
}
prop {
api_name: "operation_enroll_error"
type: Integer
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
}
prop {
api_name: "operation_enroll_latency"
type: IntegerList
access: ReadWrite
prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
}
prop {
api_name: "sensor_id"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.sensor_id"
}
prop {
api_name: "sensor_location"
type: String
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
}
prop {
api_name: "sensor_strength"
type: Integer
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.sensor_strength"
}
prop {
api_name: "type"
type: String
access: ReadWrite
prop_name: "persist.vendor.fingerprint.virtual.type"
enum_values: "default|rear|udfps|side"
}
}

View File

@@ -0,0 +1,7 @@
service vendor.fingerprint-default /vendor/bin/hw/android.hardware.biometrics.fingerprint-service.default default
class hal
user nobody
group nobody
interface aidl android.hardware.biometrics.fingerprint.IFingerprint/default
oneshot
disabled

View File

@@ -1,7 +1,10 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.biometrics.fingerprint</name>
<version>5</version>
<fqname>IFingerprint/virtual</fqname>
<version>4</version>
<interface>
<name>IFingerprint</name>
<instance>default</instance>
</interface>
</hal>
</manifest>

View File

@@ -1,7 +0,0 @@
service vendor.fingerprint-example /apex/com.android.hardware.biometrics.fingerprint.virtual/bin/hw/android.hardware.biometrics.fingerprint-service.example
class hal
user nobody
group nobody
interface aidl android.hardware.biometrics.fingerprint.IFingerprint/virtual
oneshot
disabled

View File

@@ -0,0 +1,7 @@
service fingerprint-virtual /apex/com.android.hardware.biometrics.fingerprint.virtual/bin/hw/android.hardware.biometrics.fingerprint-service.example virtual
class hal
user nobody
group nobody
interface aidl android.hardware.biometrics.fingerprint.virtualhal.IVirtualHal/virtual
oneshot
disabled

View File

@@ -7,7 +7,7 @@ owner: Vendor
prop {
prop_name: "persist.vendor.fingerprint.virtual.type"
type: String
scope: Internal
scope: Public
access: ReadWrite
enum_values: "default|rear|udfps|side"
api_name: "type"
@@ -17,7 +17,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.enrollments"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "enrollments"
}
@@ -27,7 +27,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.enrollment_hit"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "enrollment_hit"
}
@@ -42,7 +42,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.next_enrollment"
type: String
scope: Internal
scope: Public
access: ReadWrite
api_name: "next_enrollment"
}
@@ -51,7 +51,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
type: Long
scope: Internal
scope: Public
access: ReadWrite
api_name: "authenticator_id"
}
@@ -60,7 +60,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.challenge"
type: Long
scope: Internal
scope: Public
access: ReadWrite
api_name: "challenge"
}
@@ -69,7 +69,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_fails"
}
@@ -82,7 +82,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_detect_interaction_error"
}
@@ -91,7 +91,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_enroll_error"
}
@@ -104,7 +104,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_latency"
}
@@ -114,7 +114,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_detect_interaction_latency"
}
@@ -124,7 +124,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
type: IntegerList
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_enroll_latency"
}
@@ -134,7 +134,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_duration"
}
@@ -143,7 +143,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_error"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_error"
}
@@ -153,7 +153,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
type: String
scope: Internal
scope: Public
access: ReadWrite
api_name: "sensor_location"
}
@@ -162,7 +162,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired"
type: String
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_authenticate_acquired"
}
@@ -172,7 +172,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_detect_interaction_duration"
}
@@ -184,7 +184,7 @@ prop {
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired"
type: String
scope: Internal
scope: Public
access: ReadWrite
api_name: "operation_detect_interaction_acquired"
}
@@ -193,7 +193,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_id"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "sensor_id"
}
@@ -203,7 +203,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_strength"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "sensor_strength"
}
@@ -213,7 +213,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.max_enrollments"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "max_enrollments"
}
@@ -222,7 +222,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "navigation_guesture"
}
@@ -231,7 +231,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.detect_interaction"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "detect_interaction"
}
@@ -240,7 +240,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "display_touch"
}
@@ -249,7 +249,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "control_illumination"
}
@@ -258,7 +258,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.lockout"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout"
}
@@ -267,7 +267,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.lockout_enable"
type: Boolean
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_enable"
}
@@ -276,7 +276,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_threshold"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_timed_threshold"
}
@@ -285,7 +285,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_duration"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_timed_duration"
}
@@ -294,7 +294,7 @@ prop {
prop {
prop_name: "persist.vendor.fingerprint.virtual.lockout_permanent_threshold"
type: Integer
scope: Internal
scope: Public
access: ReadWrite
api_name: "lockout_permanent_threshold"
}

View File

@@ -110,7 +110,7 @@ class FakeFingerprintEngine {
std::pair<Error, int32_t> convertError(int32_t code);
int32_t getRandomInRange(int32_t bound1, int32_t bound2);
bool checkSensorLockout(ISessionCallback*);
void clearLockout(ISessionCallback* cb);
void clearLockout(ISessionCallback* cb, bool dueToTimeout = false);
void waitForFingerDown(ISessionCallback* cb, const std::future<void>& cancel);
FakeLockoutTracker mLockoutTracker;

View File

@@ -24,12 +24,12 @@ namespace aidl::android::hardware::biometrics::fingerprint {
class FakeLockoutTracker {
public:
FakeLockoutTracker() : mFailedCount(0) {}
FakeLockoutTracker() : mFailedCount(0), mFailedCountTimed(0) {}
~FakeLockoutTracker() {}
enum class LockoutMode : int8_t { kNone = 0, kTimed, kPermanent };
void reset();
void reset(bool dueToTimeout = false);
LockoutMode getMode();
void addFailedAttempt();
int64_t getLockoutTimeLeft();
@@ -44,6 +44,7 @@ class FakeLockoutTracker {
private:
int32_t mFailedCount;
int32_t mFailedCountTimed;
int64_t mLockoutTimedStart;
LockoutMode mCurrentMode;
};

View File

@@ -16,21 +16,21 @@
#pragma once
#include <aidl/android/hardware/biometrics/fingerprint/BnVirtualHal.h>
#include <aidl/android/hardware/biometrics/fingerprint/virtualhal/BnVirtualHal.h>
#include "Fingerprint.h"
namespace aidl::android::hardware::biometrics::fingerprint {
using namespace virtualhal;
class VirtualHal : public BnVirtualHal {
public:
VirtualHal(Fingerprint* fp) : mFp(fp) {}
VirtualHal(std::shared_ptr<Fingerprint> fp) : mFp(fp) {}
::ndk::ScopedAStatus setEnrollments(const std::vector<int32_t>& in_id) override;
::ndk::ScopedAStatus setEnrollmentHit(int32_t in_hit_id) override;
::ndk::ScopedAStatus setNextEnrollment(
const ::aidl::android::hardware::biometrics::fingerprint::NextEnrollment&
in_next_enrollment) override;
::ndk::ScopedAStatus setNextEnrollment(const NextEnrollment& in_next_enrollment) override;
::ndk::ScopedAStatus setAuthenticatorId(int64_t in_id) override;
::ndk::ScopedAStatus setChallenge(int64_t in_challenge) override;
::ndk::ScopedAStatus setOperationAuthenticateFails(bool in_fail) override;
@@ -67,12 +67,15 @@ class VirtualHal : public BnVirtualHal {
::ndk::ScopedAStatus setDetectInteraction(bool in_v) override;
::ndk::ScopedAStatus setDisplayTouch(bool in_v) override;
::ndk::ScopedAStatus setControlIllumination(bool in_v) override;
::ndk::ScopedAStatus getFingerprintHal(
std::shared_ptr<::aidl::android::hardware::biometrics::fingerprint::IFingerprint>*
_aidl_return);
private:
OptIntVec intVec2OptIntVec(const std::vector<int32_t>& intVec);
OptIntVec acquiredInfoVec2OptIntVec(const std::vector<AcquiredInfoAndVendorCode>& intVec);
::ndk::ScopedAStatus sanityCheckLatency(const std::vector<int32_t>& in_latency);
Fingerprint* mFp;
std::shared_ptr<Fingerprint> mFp;
};
} // namespace aidl::android::hardware::biometrics::fingerprint

View File

@@ -24,21 +24,38 @@
using aidl::android::hardware::biometrics::fingerprint::Fingerprint;
using aidl::android::hardware::biometrics::fingerprint::VirtualHal;
int main() {
LOG(INFO) << "Fingerprint HAL started";
int main(int argc, char** argv) {
if (argc < 2) {
LOG(ERROR) << "Missing argument -> exiting";
return EXIT_FAILURE;
}
LOG(INFO) << "Fingerprint HAL started: " << argv[1];
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<Fingerprint> hal = ndk::SharedRefBase::make<Fingerprint>();
auto binder = hal->asBinder();
std::shared_ptr<VirtualHal> hal_ext = ndk::SharedRefBase::make<VirtualHal>(hal.get());
auto binder_ext = hal_ext->asBinder();
std::shared_ptr<VirtualHal> hal_vhal = ndk::SharedRefBase::make<VirtualHal>(hal);
if (hal->connected()) {
CHECK(STATUS_OK == AIBinder_setExtension(binder.get(), binder_ext.get()));
const std::string instance = std::string(Fingerprint::descriptor) + "/virtual";
binder_status_t status =
AServiceManager_registerLazyService(binder.get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
if (strcmp(argv[1], "default") == 0) {
const std::string instance = std::string(Fingerprint::descriptor) + "/default";
auto binder = hal->asBinder();
auto binder_ext = hal_vhal->asBinder();
CHECK(STATUS_OK == AIBinder_setExtension(binder.get(), binder_ext.get()));
binder_status_t status =
AServiceManager_registerLazyService(binder.get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
LOG(INFO) << "started IFingerprint/default";
} else if (strcmp(argv[1], "virtual") == 0) {
const std::string instance = std::string(VirtualHal::descriptor) + "/virtual";
auto binder = hal_vhal->asBinder();
binder_status_t status =
AServiceManager_registerLazyService(binder.get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
LOG(INFO) << "started IVirtualHal/virtual";
} else {
LOG(ERROR) << "Unexpected argument: " << argv[1];
return EXIT_FAILURE;
}
AServiceManager_forceLazyServicesPersist(true);
} else {
LOG(ERROR) << "Fingerprint HAL is not connected";

View File

@@ -75,7 +75,7 @@ TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimed) {
prevTimeLeft = currTimeLeft;
}
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
mLockoutTracker.reset();
mLockoutTracker.reset(true);
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptPermanent) {

View File

@@ -35,7 +35,7 @@ class VirtualHalTest : public ::testing::Test {
protected:
void SetUp() override {
mHal = ndk::SharedRefBase::make<Fingerprint>();
mVhal = ndk::SharedRefBase::make<VirtualHal>(mHal.get());
mVhal = ndk::SharedRefBase::make<VirtualHal>(mHal);
ASSERT_TRUE(mVhal != nullptr);
mHal->resetConfigToDefault();
}

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