Merge "Add IPTV default implementation" into main
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 1d96a0e..ad816c7 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -40,7 +40,10 @@
"general-tests",
"vts",
],
- srcs: [":effectCommonFile"],
+ srcs: [
+ ":effectCommonFile",
+ "TestUtils.cpp",
+ ],
}
cc_test {
diff --git a/audio/aidl/vts/TestUtils.cpp b/audio/aidl/vts/TestUtils.cpp
new file mode 100644
index 0000000..f018468
--- /dev/null
+++ b/audio/aidl/vts/TestUtils.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestUtils.h"
+
+#define LOG_TAG "VtsHalAudio_TestUtils"
+
+#include <android-base/logging.h>
+
+namespace android::hardware::audio::common::testing {
+
+namespace detail {
+void TestExecutionTracer::OnTestStart(const ::testing::TestInfo& test_info) {
+ TraceTestState("Started", test_info);
+}
+
+void TestExecutionTracer::OnTestEnd(const ::testing::TestInfo& test_info) {
+ TraceTestState("Completed", test_info);
+}
+
+void TestExecutionTracer::OnTestPartResult(const ::testing::TestPartResult& result) {
+ LOG(INFO) << result;
+}
+
+void TestExecutionTracer::TraceTestState(const std::string& state,
+ const ::testing::TestInfo& test_info) {
+ LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
+}
+}
+}
\ No newline at end of file
diff --git a/audio/aidl/vts/TestUtils.h b/audio/aidl/vts/TestUtils.h
index b559669..191e980 100644
--- a/audio/aidl/vts/TestUtils.h
+++ b/audio/aidl/vts/TestUtils.h
@@ -22,11 +22,21 @@
#include <android/binder_auto_utils.h>
#include <gtest/gtest.h>
+#include <system/audio_aidl_utils.h>
namespace android::hardware::audio::common::testing {
namespace detail {
+class TestExecutionTracer : public ::testing::EmptyTestEventListener {
+ public:
+ void OnTestStart(const ::testing::TestInfo& test_info) override;
+ void OnTestEnd(const ::testing::TestInfo& test_info) override;
+ void OnTestPartResult(const ::testing::TestPartResult& result) override;
+ private:
+ static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info);
+};
+
inline ::testing::AssertionResult assertIsOk(const char* expr, const ::ndk::ScopedAStatus& status) {
if (status.isOk()) {
return ::testing::AssertionSuccess();
diff --git a/audio/aidl/vts/VtsHalAECTargetTest.cpp b/audio/aidl/vts/VtsHalAECTargetTest.cpp
index 0354e3c..39168b1 100644
--- a/audio/aidl/vts/VtsHalAECTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAECTargetTest.cpp
@@ -34,6 +34,7 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::Range;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
enum ParamName { PARAM_INSTANCE_NAME, PARAM_ECHO_DELAY, PARAM_MOBILE_MODE };
using AECParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>,
@@ -178,6 +179,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalAGC1TargetTest.cpp b/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
index 65c6a8f..6066025 100644
--- a/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
@@ -28,6 +28,7 @@
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
enum ParamName {
PARAM_INSTANCE_NAME,
@@ -189,6 +190,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalAGC2TargetTest.cpp b/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
index 46a569e..8793e4c 100644
--- a/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
@@ -29,6 +29,7 @@
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
enum ParamName {
PARAM_INSTANCE_NAME,
@@ -194,6 +195,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 9c52d19..53e51f4 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -108,6 +108,7 @@
using aidl::android::media::audio::common::Void;
using android::hardware::audio::common::StreamLogic;
using android::hardware::audio::common::StreamWorker;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
using ndk::enum_range;
using ndk::ScopedAStatus;
@@ -4444,21 +4445,6 @@
::testing::ValuesIn(getRemoteSubmixModuleInstance()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioModuleRemoteSubmix);
-class TestExecutionTracer : public ::testing::EmptyTestEventListener {
- public:
- void OnTestStart(const ::testing::TestInfo& test_info) override {
- TraceTestState("Started", test_info);
- }
- void OnTestEnd(const ::testing::TestInfo& test_info) override {
- TraceTestState("Completed", test_info);
- }
-
- private:
- static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) {
- LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
- }
-};
-
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
index 225640e..523f20d 100644
--- a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
@@ -52,6 +52,7 @@
using aidl::android::media::audio::common::AudioSource;
using aidl::android::media::audio::common::AudioStreamType;
using aidl::android::media::audio::common::AudioUuid;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
/// Effect factory testing.
class EffectFactoryTest : public testing::TestWithParam<std::string> {
@@ -303,6 +304,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index 1876756..ca1cea9 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -51,6 +51,7 @@
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioMode;
using aidl::android::media::audio::common::AudioSource;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
enum ParamName { PARAM_INSTANCE_NAME };
using EffectTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>>;
@@ -907,6 +908,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
index afddb84..2d9a233 100644
--- a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
@@ -32,7 +32,7 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::Range;
-
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
* VtsAudioEffectTargetTest.
@@ -155,6 +155,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
index 7a2f31b..c01a9a2 100644
--- a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
@@ -28,7 +28,7 @@
using aidl::android::hardware::audio::effect::IEffect;
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.
@@ -136,6 +136,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index 5509c76..2650f49 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -36,6 +36,7 @@
using aidl::android::hardware::audio::effect::IEffect;
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
@@ -996,6 +997,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
index f2ef185..474b361 100644
--- a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
@@ -28,6 +28,7 @@
using aidl::android::hardware::audio::effect::IEffect;
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
@@ -536,6 +537,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
index 37e7c0a..09396d1 100644
--- a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
@@ -46,6 +46,7 @@
using aidl::android::hardware::audio::effect::IEffect;
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 effect (equalizer) parameter checking, general IEffect interfaces
@@ -220,6 +221,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
index b33234b..5a32398 100644
--- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
@@ -33,6 +33,7 @@
using aidl::android::hardware::audio::effect::IEffect;
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
@@ -431,6 +432,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
index cbb80a9..925f9ec 100644
--- a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
@@ -30,28 +30,21 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::LoudnessEnhancer;
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_GAIN_MB };
-using LoudnessEnhancerParamTestParam =
- std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int>;
+static constexpr float kMaxAudioSample = 1;
+static constexpr int kZeroGain = 0;
+static constexpr int kMaxGain = std::numeric_limits<int>::max();
+static constexpr int kMinGain = std::numeric_limits<int>::min();
+static constexpr float kAbsError = 0.0001;
// Every int 32 bit value is a valid gain, so testing the corner cases and one regular value.
// TODO : Update the test values once range/capability is updated by implementation.
-const std::vector<int> kGainMbValues = {std::numeric_limits<int>::min(), 100,
- std::numeric_limits<int>::max()};
+static const std::vector<int> kGainMbValues = {kMinGain, -100, -50, kZeroGain, 50, 100, kMaxGain};
-class LoudnessEnhancerParamTest : public ::testing::TestWithParam<LoudnessEnhancerParamTestParam>,
- public EffectHelper {
+class LoudnessEnhancerEffectHelper : public EffectHelper {
public:
- LoudnessEnhancerParamTest() : mParamGainMb(std::get<PARAM_GAIN_MB>(GetParam())) {
- std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
- }
-
- void SetUp() override {
+ void SetUpLoudnessEnhancer() {
ASSERT_NE(nullptr, mFactory);
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
@@ -59,13 +52,14 @@
Parameter::Common common = EffectHelper::createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
- IEffect::OpenEffectReturn ret;
- ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE));
ASSERT_NE(nullptr, mEffect);
}
- void TearDown() override {
+
+ void TearDownLoudnessEnhancer() {
ASSERT_NO_FATAL_FAILURE(close(mEffect));
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+ mOpenEffectReturn = IEffect::OpenEffectReturn{};
}
Parameter::Specific getDefaultParamSpecific() {
@@ -75,52 +69,230 @@
return specific;
}
- static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
- std::shared_ptr<IFactory> mFactory;
- std::shared_ptr<IEffect> mEffect;
- Descriptor mDescriptor;
- int mParamGainMb = 0;
+ Parameter createLoudnessParam(int gainMb) {
+ LoudnessEnhancer le;
+ le.set<LoudnessEnhancer::gainMb>(gainMb);
+ Parameter param;
+ Parameter::Specific specific;
+ specific.set<Parameter::Specific::loudnessEnhancer>(le);
+ param.set<Parameter::specific>(specific);
+ return param;
+ }
- void SetAndGetParameters() {
- for (auto& it : mTags) {
- auto& tag = it.first;
- auto& le = it.second;
-
- // set parameter
- Parameter expectParam;
- Parameter::Specific specific;
- specific.set<Parameter::Specific::loudnessEnhancer>(le);
- expectParam.set<Parameter::specific>(specific);
- // All values are valid, set parameter should succeed
- EXPECT_STATUS(EX_NONE, mEffect->setParameter(expectParam)) << expectParam.toString();
-
- // get parameter
- Parameter getParam;
- Parameter::Id id;
- LoudnessEnhancer::Id leId;
- leId.set<LoudnessEnhancer::Id::commonTag>(tag);
- id.set<Parameter::Id::loudnessEnhancerTag>(leId);
- EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
-
- EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString()
- << "\ngetParam:" << getParam.toString();
+ binder_exception_t isGainValid(int gainMb) {
+ LoudnessEnhancer le;
+ le.set<LoudnessEnhancer::gainMb>(gainMb);
+ if (isParameterValid<LoudnessEnhancer, Range::loudnessEnhancer>(le, mDescriptor)) {
+ return EX_NONE;
+ } else {
+ return EX_ILLEGAL_ARGUMENT;
}
}
- void addGainMbParam(int gainMb) {
- LoudnessEnhancer le;
- le.set<LoudnessEnhancer::gainMb>(gainMb);
- mTags.push_back({LoudnessEnhancer::gainMb, le});
+ void setParameters(int gain, binder_exception_t expected) {
+ // set parameter
+ auto param = createLoudnessParam(gain);
+ EXPECT_STATUS(expected, mEffect->setParameter(param)) << param.toString();
}
- private:
- std::vector<std::pair<LoudnessEnhancer::Tag, LoudnessEnhancer>> mTags;
- void CleanUp() { mTags.clear(); }
+ void validateParameters(int gain) {
+ // get parameter
+ LoudnessEnhancer::Id leId;
+ Parameter getParam;
+ Parameter::Id id;
+
+ LoudnessEnhancer::Tag tag(LoudnessEnhancer::gainMb);
+ leId.set<LoudnessEnhancer::Id::commonTag>(tag);
+ id.set<Parameter::Id::loudnessEnhancerTag>(leId);
+ EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
+ auto expectedParam = createLoudnessParam(gain);
+ EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString()
+ << "\ngetParam:" << getParam.toString();
+ }
+
+ static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
+ IEffect::OpenEffectReturn mOpenEffectReturn;
+ std::shared_ptr<IFactory> mFactory;
+ std::shared_ptr<IEffect> mEffect;
+ Descriptor mDescriptor;
+};
+
+/**
+ * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
+ * VtsAudioEffectTargetTest.
+ */
+enum ParamName { PARAM_INSTANCE_NAME, PARAM_GAIN_MB };
+using LoudnessEnhancerParamTestParam =
+ std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int>;
+
+class LoudnessEnhancerParamTest : public ::testing::TestWithParam<LoudnessEnhancerParamTestParam>,
+ public LoudnessEnhancerEffectHelper {
+ public:
+ LoudnessEnhancerParamTest() : mParamGainMb(std::get<PARAM_GAIN_MB>(GetParam())) {
+ std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
+ }
+
+ void SetUp() override { SetUpLoudnessEnhancer(); }
+ void TearDown() override { TearDownLoudnessEnhancer(); }
+ int mParamGainMb = 0;
};
TEST_P(LoudnessEnhancerParamTest, SetAndGetGainMb) {
- EXPECT_NO_FATAL_FAILURE(addGainMbParam(mParamGainMb));
- SetAndGetParameters();
+ binder_exception_t expected = isGainValid(mParamGainMb);
+ setParameters(mParamGainMb, expected);
+ if (expected == EX_NONE) {
+ validateParameters(mParamGainMb);
+ }
+}
+
+using LoudnessEnhancerDataTestParam = std::pair<std::shared_ptr<IFactory>, Descriptor>;
+
+class LoudnessEnhancerDataTest : public ::testing::TestWithParam<LoudnessEnhancerDataTestParam>,
+ public LoudnessEnhancerEffectHelper {
+ public:
+ LoudnessEnhancerDataTest() {
+ std::tie(mFactory, mDescriptor) = GetParam();
+ generateInputBuffer();
+ mOutputBuffer.resize(kBufferSize);
+ }
+
+ void SetUp() override {
+ SetUpLoudnessEnhancer();
+
+ // Creating AidlMessageQueues
+ mStatusMQ = std::make_unique<EffectHelper::StatusMQ>(mOpenEffectReturn.statusMQ);
+ mInputMQ = std::make_unique<EffectHelper::DataMQ>(mOpenEffectReturn.inputDataMQ);
+ mOutputMQ = std::make_unique<EffectHelper::DataMQ>(mOpenEffectReturn.outputDataMQ);
+ }
+
+ void TearDown() override { TearDownLoudnessEnhancer(); }
+
+ // Fill inputBuffer with random values between -kMaxAudioSample to kMaxAudioSample
+ void generateInputBuffer() {
+ for (size_t i = 0; i < kBufferSize; i++) {
+ mInputBuffer.push_back(((static_cast<float>(std::rand()) / RAND_MAX) * 2 - 1) *
+ kMaxAudioSample);
+ }
+ }
+
+ // Add gains to the mInputBuffer and store processed output to mOutputBuffer
+ void processAndWriteToOutput() {
+ // Check AidlMessageQueues are not null
+ ASSERT_TRUE(mStatusMQ->isValid());
+ ASSERT_TRUE(mInputMQ->isValid());
+ ASSERT_TRUE(mOutputMQ->isValid());
+
+ // Enabling the process
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING));
+
+ // Write from buffer to message queues and calling process
+ EXPECT_NO_FATAL_FAILURE(EffectHelper::writeToFmq(mStatusMQ, mInputMQ, mInputBuffer));
+
+ // Read the updated message queues into buffer
+ EXPECT_NO_FATAL_FAILURE(EffectHelper::readFromFmq(mStatusMQ, 1, mOutputMQ,
+ mOutputBuffer.size(), mOutputBuffer));
+
+ // Disable the process
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
+ }
+
+ void assertGreaterGain(const std::vector<float>& first, const std::vector<float>& second) {
+ for (size_t i = 0; i < first.size(); i++) {
+ if (first[i] != 0) {
+ ASSERT_GT(abs(first[i]), abs(second[i]));
+
+ } else {
+ ASSERT_EQ(first[i], second[i]);
+ }
+ }
+ }
+
+ void assertSequentialGains(const std::vector<int>& gainValues, bool isIncreasing) {
+ std::vector<float> baseOutput(kBufferSize);
+
+ // Process a reference output buffer with 0 gain which gives compressed input values
+ binder_exception_t expected;
+ expected = isGainValid(kZeroGain);
+ ASSERT_EQ(expected, EX_NONE);
+ setParameters(kZeroGain, expected);
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput());
+ baseOutput = mOutputBuffer;
+
+ // Compare the outputs for increasing gain
+ for (int gain : gainValues) {
+ // Setting the parameters
+ binder_exception_t expected = isGainValid(gain);
+ if (expected != EX_NONE) {
+ GTEST_SKIP() << "Gains not supported.";
+ }
+ setParameters(gain, expected);
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput());
+
+ // Compare the mOutputBuffer values with baseOutput and update it
+ if (isIncreasing) {
+ ASSERT_NO_FATAL_FAILURE(assertGreaterGain(mOutputBuffer, baseOutput));
+ } else {
+ ASSERT_NO_FATAL_FAILURE(assertGreaterGain(baseOutput, mOutputBuffer));
+ }
+
+ baseOutput = mOutputBuffer;
+ }
+ }
+
+ std::unique_ptr<StatusMQ> mStatusMQ;
+ std::unique_ptr<DataMQ> mInputMQ;
+ std::unique_ptr<DataMQ> mOutputMQ;
+
+ std::vector<float> mInputBuffer;
+ std::vector<float> mOutputBuffer;
+ static constexpr float kBufferSize = 128;
+};
+
+TEST_P(LoudnessEnhancerDataTest, IncreasingGains) {
+ static const std::vector<int> kIncreasingGains = {50, 100};
+
+ assertSequentialGains(kIncreasingGains, true /*isIncreasing*/);
+}
+
+TEST_P(LoudnessEnhancerDataTest, DecreasingGains) {
+ static const std::vector<int> kDecreasingGains = {-50, -100};
+
+ assertSequentialGains(kDecreasingGains, false /*isIncreasing*/);
+}
+
+TEST_P(LoudnessEnhancerDataTest, MinimumGain) {
+ // Setting the parameters
+ binder_exception_t expected = isGainValid(kMinGain);
+ if (expected != EX_NONE) {
+ GTEST_SKIP() << "Minimum integer value not supported";
+ }
+ setParameters(kMinGain, expected);
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput());
+
+ // Validate that mOutputBuffer has 0 values for INT_MIN gain
+ for (size_t i = 0; i < mOutputBuffer.size(); i++) {
+ ASSERT_FLOAT_EQ(mOutputBuffer[i], 0);
+ }
+}
+
+TEST_P(LoudnessEnhancerDataTest, MaximumGain) {
+ // Setting the parameters
+ binder_exception_t expected = isGainValid(kMaxGain);
+ if (expected != EX_NONE) {
+ GTEST_SKIP() << "Maximum integer value not supported";
+ }
+ setParameters(kMaxGain, expected);
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput());
+
+ // Validate that mOutputBuffer reaches to kMaxAudioSample for INT_MAX gain
+ for (size_t i = 0; i < mOutputBuffer.size(); i++) {
+ if (mInputBuffer[i] != 0) {
+ EXPECT_NEAR(kMaxAudioSample, abs(mOutputBuffer[i]), kAbsError);
+ } else {
+ ASSERT_EQ(mOutputBuffer[i], mInputBuffer[i]);
+ }
+ }
}
INSTANTIATE_TEST_SUITE_P(
@@ -139,8 +311,23 @@
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LoudnessEnhancerParamTest);
+INSTANTIATE_TEST_SUITE_P(
+ LoudnessEnhancerTest, LoudnessEnhancerDataTest,
+ testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+ IFactory::descriptor, getEffectTypeUuidLoudnessEnhancer())),
+ [](const testing::TestParamInfo<LoudnessEnhancerDataTest::ParamType>& info) {
+ auto descriptor = info.param;
+ std::string name = getPrefix(descriptor.second);
+ std::replace_if(
+ name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+ return name;
+ });
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LoudnessEnhancerDataTest);
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalNSTargetTest.cpp b/audio/aidl/vts/VtsHalNSTargetTest.cpp
index 624d5d2..12d56b0 100644
--- a/audio/aidl/vts/VtsHalNSTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalNSTargetTest.cpp
@@ -32,6 +32,7 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::NoiseSuppression;
using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
enum ParamName { PARAM_INSTANCE_NAME, PARAM_LEVEL, PARAM_TYPE };
using NSParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>,
@@ -171,6 +172,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
index 3056c6c..57eda09 100644
--- a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
@@ -29,6 +29,7 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::PresetReverb;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -147,6 +148,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
index 07a9fa4..3e39d3a 100644
--- a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
@@ -28,6 +28,7 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::Virtualizer;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -151,6 +152,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
index 903ba69..1b8352b 100644
--- a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
@@ -31,6 +31,7 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::Visualizer;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -207,6 +208,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
index 0b5b9fc..257100b 100644
--- a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
@@ -28,6 +28,7 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::Volume;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -159,6 +160,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/common/all-versions/test/utility/src/ValidateXml.cpp b/audio/common/all-versions/test/utility/src/ValidateXml.cpp
index 4d6f003..b7e685b 100644
--- a/audio/common/all-versions/test/utility/src/ValidateXml.cpp
+++ b/audio/common/all-versions/test/utility/src/ValidateXml.cpp
@@ -19,6 +19,7 @@
#include <numeric>
+#include <libxml/parser.h>
#define LIBXML_SCHEMAS_ENABLED
#include <libxml/xmlschemastypes.h>
#define LIBXML_XINCLUDE_ENABLED
diff --git a/audio/common/all-versions/test/utility/tests/utility_tests.cpp b/audio/common/all-versions/test/utility/tests/utility_tests.cpp
index c523066..c2bcfbc 100644
--- a/audio/common/all-versions/test/utility/tests/utility_tests.cpp
+++ b/audio/common/all-versions/test/utility/tests/utility_tests.cpp
@@ -70,6 +70,7 @@
std::string substitute(const char* fmt, const char* param) {
std::string buffer(static_cast<size_t>(strlen(fmt) + strlen(param)), '\0');
snprintf(buffer.data(), buffer.size(), fmt, param);
+ buffer.resize(strlen(buffer.c_str()));
return buffer;
}
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index 02ad8bb..e1a90cb 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -51,6 +51,9 @@
"include-filter": "com.android.car.hal.fakevhal.FakeVehicleStubUnitTest"
}
]
+ },
+ {
+ "name": "VehicleHalProtoMessageConverterTest"
}
]
}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SubscribeOptions.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SubscribeOptions.aidl
index 91e7c14..1b1696b 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SubscribeOptions.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SubscribeOptions.aidl
@@ -37,4 +37,6 @@
int propId;
int[] areaIds;
float sampleRate;
+ float resolution = 0.0f;
+ boolean enableVariableUpdateRate;
}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index 3322769..08d4ee4 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -43,4 +43,5 @@
float maxFloatValue;
@nullable long[] supportedEnumValues;
android.hardware.automotive.vehicle.VehiclePropertyAccess access = android.hardware.automotive.vehicle.VehiclePropertyAccess.NONE;
+ boolean supportVariableUpdateRate;
}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SubscribeOptions.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SubscribeOptions.aidl
index e68f7e3..69f6190 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SubscribeOptions.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SubscribeOptions.aidl
@@ -39,4 +39,29 @@
* This value indicates how many updates per second client wants to receive.
*/
float sampleRate;
+
+ /**
+ * Requested resolution of property updates.
+ *
+ * This value indicates the resolution at which continuous property updates should be sent to
+ * the platform. For example, if resolution is 0.01, the subscribed property value should be
+ * rounded to two decimal places. If the incoming resolution value is not an integer multiple of
+ * 10, VHAL should return a StatusCode::INVALID_ARG.
+ */
+ float resolution = 0.0f;
+
+ /**
+ * Whether to enable variable update rate.
+ *
+ * This only applies for continuous property. If variable update rate is
+ * enabled, for each given areaId, if VHAL supports variable update rate for
+ * the [propId, areaId], VHAL must ignore duplicate property value events
+ * and only sends changed value events (a.k.a treat continuous as an
+ * on-change property).
+ *
+ * If VHAL does not support variable update rate for the [propId, areaId],
+ * indicated by 'supportVariableUpdateRate' in 'VehicleAreaConfig', or if
+ * this property is not a continuous property, this option must be ignored.
+ */
+ boolean enableVariableUpdateRate;
}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index 9fd9bda..08863b2 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -70,4 +70,31 @@
* well.
*/
VehiclePropertyAccess access = VehiclePropertyAccess.NONE;
+
+ /**
+ * Whether variable update rate is supported.
+ *
+ * This applies for continuous property only.
+ *
+ * It is HIGHLY RECOMMENDED to support variable update rate for all non-heartbeat continuous
+ * properties for better performance.
+ *
+ * If variable update rate is supported and 'enableVariableUpdateRate' is true in subscribe
+ * options, VHAL must only sends property update event when the property's value changes
+ * (a.k.a treat continuous as an on-change property).
+ *
+ * E.g. if the client is subscribing at 5hz at time 0. If the property's value is 0 initially
+ * and becomes 1 after 1 second.
+
+ * If variable update rate is not enabled, VHAL clients will receive 5 property change events
+ * with value 0 and 5 events with value 1 after 2 seconds.
+ *
+ * If variable update rate is enabled, VHAL clients will receive 1 property change event
+ * with value 1 at time 1s. VHAL may/may not send a property event for the initial value (e.g.
+ * a property change event with value 0 at time 0s). VHAL client must not rely on the first
+ * property event, and must use getValues to fetch the initial value. In fact, car service is
+ * using getValues to fetch the initial value, convert it to a property event and deliver to
+ * car service clients.
+ */
+ boolean supportVariableUpdateRate;
}
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
index 39ce10e..82dc8a6 100644
--- a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
@@ -275,6 +275,16 @@
}
template <>
+Result<bool> JsonValueParser::convertValueToType<bool>(const std::string& fieldName,
+ const Json::Value& value) {
+ if (!value.isBool()) {
+ return Error() << "The value: " << value << " for field: " << fieldName
+ << " is not in correct type, expect bool";
+ }
+ return value.asBool();
+}
+
+template <>
Result<int32_t> JsonValueParser::convertValueToType<int32_t>(const std::string& fieldName,
const Json::Value& value) {
if (!value.isInt()) {
@@ -531,6 +541,12 @@
tryParseJsonValueToVariable(jsonAreaConfig, "maxFloatValue", /*optional=*/true,
&areaConfig.maxFloatValue, errors);
+ // By default we support variable update rate for all properties except it is explicitly
+ // disabled.
+ areaConfig.supportVariableUpdateRate = true;
+ tryParseJsonValueToVariable(jsonAreaConfig, "supportVariableUpdateRate", /*optional=*/true,
+ &areaConfig.supportVariableUpdateRate, errors);
+
std::vector<int64_t> supportedEnumValues;
tryParseJsonArrayToVariable(jsonAreaConfig, "supportedEnumValues", /*optional=*/true,
&supportedEnumValues, errors);
@@ -585,6 +601,16 @@
if (errors->size() != initialErrorCount) {
return std::nullopt;
}
+
+ // If there is no area config, by default we allow variable update rate, so we have to add
+ // a global area config.
+ if (configDecl.config.areaConfigs.size() == 0) {
+ VehicleAreaConfig areaConfig = {
+ .areaId = 0,
+ .supportVariableUpdateRate = true,
+ };
+ configDecl.config.areaConfigs.push_back(std::move(areaConfig));
+ }
return configDecl;
}
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 6c8d59c..d3bb60c 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -3579,7 +3579,13 @@
"property": "VehicleProperty::WATCHDOG_TERMINATED_PROCESS"
},
{
- "property": "VehicleProperty::VHAL_HEARTBEAT"
+ "property": "VehicleProperty::VHAL_HEARTBEAT",
+ "areas": [
+ {
+ "areaId": 0,
+ "supportVariableUpdateRate": false
+ }
+ ]
},
{
"property": "VehicleProperty::CLUSTER_SWITCH_UI",
@@ -3641,6 +3647,12 @@
0,
16
],
+ "areas": [
+ {
+ "areaId": 0,
+ "supportVariableUpdateRate": false
+ }
+ ],
"comment": "configArray specifies it consists of int64[2] and byte[16]."
},
{
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index 844bea5..26fdee6 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -91,9 +91,13 @@
void registerOnPropertySetErrorEvent(
std::unique_ptr<const PropertySetErrorCallback> callback) override;
- // Update the sample rate for the [propId, areaId] pair.
- aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
- int32_t propId, int32_t areaId, float sampleRate) override;
+ // Subscribe to a new [propId, areaId] or change the update rate.
+ aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
+ aidl::android::hardware::automotive::vehicle::SubscribeOptions options) override;
+
+ // Unsubscribe to a [propId, areaId].
+ aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(int32_t propId,
+ int32_t areaId) override;
protected:
// mValuePool is also used in mServerSidePropStore.
@@ -154,6 +158,7 @@
mRecurrentActions GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, VehiclePropValuePool::RecyclableType, PropIdAreaIdHash>
mSavedProps GUARDED_BY(mLock);
+ std::unordered_set<PropIdAreaId, PropIdAreaIdHash> mSubOnChangePropIdAreaIds GUARDED_BY(mLock);
// PendingRequestHandler is thread-safe.
mutable PendingRequestHandler<GetValuesCallback,
aidl::android::hardware::automotive::vehicle::GetValueRequest>
@@ -176,7 +181,8 @@
void storePropInitialValue(const ConfigDeclaration& config);
// The callback that would be called when a vehicle property value change happens.
void onValueChangeCallback(
- const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
+ const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)
+ EXCLUDES(mLock);
// Load the config files in format '*.json' from the directory and parse the config files
// into a map from property ID to ConfigDeclarations.
void loadPropConfigsFromDir(const std::string& dirPath,
@@ -216,6 +222,9 @@
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
bool isHvacPropAndHvacNotAvailable(int32_t propId, int32_t areaId) const;
VhalResult<void> isAdasPropertyAvailable(int32_t adasStatePropertyId) const;
+ VhalResult<void> synchronizeHvacTemp(int32_t hvacDualOnAreaId,
+ std::optional<float> newTempC) const;
+ std::optional<int32_t> getSyncedAreaIdIfHvacDualOn(int32_t hvacTemperatureSetAreaId) const;
std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations();
@@ -262,6 +271,11 @@
void generateVendorConfigs(
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>&) const;
+ aidl::android::hardware::automotive::vehicle::StatusCode subscribePropIdAreaIdLocked(
+ int32_t propId, int32_t areaId, float sampleRateHz, bool enableVariableUpdateRate,
+ const aidl::android::hardware::automotive::vehicle::VehiclePropConfig&
+ vehiclePropConfig) REQUIRES(mLock);
+
static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwInputKeyProp(
aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction action,
int32_t keyCode, int32_t targetDisplay);
@@ -275,6 +289,10 @@
static std::string genFakeDataHelp();
static std::string parseErrMsg(std::string fieldName, std::string value, std::string type);
+ static bool isVariableUpdateRateSupported(
+ const aidl::android::hardware::automotive::vehicle::VehiclePropConfig&
+ vehiclePropConfig,
+ int32_t areaId);
};
} // namespace fake
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index ee24fbd..7ff03c6 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -60,6 +60,8 @@
using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::toString;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
using ::aidl::android::hardware::automotive::vehicle::VehicleArea;
@@ -67,6 +69,7 @@
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
@@ -598,6 +601,65 @@
return {};
}
+VhalResult<void> FakeVehicleHardware::synchronizeHvacTemp(int32_t hvacDualOnAreaId,
+ std::optional<float> newTempC) const {
+ auto hvacTemperatureSetResults = mServerSidePropStore->readValuesForProperty(
+ toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+ if (!hvacTemperatureSetResults.ok()) {
+ return StatusError(StatusCode::NOT_AVAILABLE)
+ << "Failed to get HVAC_TEMPERATURE_SET, error: "
+ << getErrorMsg(hvacTemperatureSetResults);
+ }
+ auto& hvacTemperatureSetValues = hvacTemperatureSetResults.value();
+ std::optional<float> tempCToSynchronize = newTempC;
+ for (size_t i = 0; i < hvacTemperatureSetValues.size(); i++) {
+ int32_t areaId = hvacTemperatureSetValues[i]->areaId;
+ if ((hvacDualOnAreaId & areaId) != areaId) {
+ continue;
+ }
+ if (hvacTemperatureSetValues[i]->status != VehiclePropertyStatus::AVAILABLE) {
+ continue;
+ }
+ // When HVAC_DUAL_ON is initially enabled, synchronize all area IDs
+ // to the temperature of the first area ID, which is the driver's.
+ if (!tempCToSynchronize.has_value()) {
+ tempCToSynchronize = hvacTemperatureSetValues[i]->value.floatValues[0];
+ continue;
+ }
+ auto updatedValue = std::move(hvacTemperatureSetValues[i]);
+ updatedValue->value.floatValues[0] = tempCToSynchronize.value();
+ updatedValue->timestamp = elapsedRealtimeNano();
+ // This will trigger a property change event for the current hvac property value.
+ auto writeResult =
+ mServerSidePropStore->writeValue(std::move(updatedValue), /*updateStatus=*/true,
+ VehiclePropertyStore::EventMode::ALWAYS);
+ if (!writeResult.ok()) {
+ return StatusError(getErrorCode(writeResult))
+ << "Failed to write value into property store, error: "
+ << getErrorMsg(writeResult);
+ }
+ }
+ return {};
+}
+
+std::optional<int32_t> FakeVehicleHardware::getSyncedAreaIdIfHvacDualOn(
+ int32_t hvacTemperatureSetAreaId) const {
+ auto hvacDualOnResults =
+ mServerSidePropStore->readValuesForProperty(toInt(VehicleProperty::HVAC_DUAL_ON));
+ if (!hvacDualOnResults.ok()) {
+ return std::nullopt;
+ }
+ auto& hvacDualOnValues = hvacDualOnResults.value();
+ for (size_t i = 0; i < hvacDualOnValues.size(); i++) {
+ if ((hvacDualOnValues[i]->areaId & hvacTemperatureSetAreaId) == hvacTemperatureSetAreaId &&
+ hvacDualOnValues[i]->value.int32Values.size() == 1 &&
+ hvacDualOnValues[i]->value.int32Values[0] == 1) {
+ return hvacDualOnValues[i]->areaId;
+ }
+ }
+ return std::nullopt;
+}
+
FakeVehicleHardware::ValueResultType FakeVehicleHardware::getUserHalProp(
const VehiclePropValue& value) const {
auto propId = value.prop;
@@ -850,6 +912,28 @@
case toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION):
*isSpecialValue = true;
return setHvacTemperatureValueSuggestion(value);
+ case toInt(VehicleProperty::HVAC_TEMPERATURE_SET):
+ if (value.value.floatValues.size() != 1) {
+ *isSpecialValue = true;
+ return StatusError(StatusCode::INVALID_ARG)
+ << "HVAC_DUAL_ON requires only one float value";
+ }
+ if (auto hvacDualOnAreaId = getSyncedAreaIdIfHvacDualOn(value.areaId);
+ hvacDualOnAreaId.has_value()) {
+ *isSpecialValue = true;
+ return synchronizeHvacTemp(hvacDualOnAreaId.value(), value.value.floatValues[0]);
+ }
+ return {};
+ case toInt(VehicleProperty::HVAC_DUAL_ON):
+ if (value.value.int32Values.size() != 1) {
+ *isSpecialValue = true;
+ return StatusError(StatusCode::INVALID_ARG)
+ << "HVAC_DUAL_ON requires only one int32 value";
+ }
+ if (value.value.int32Values[0] == 1) {
+ synchronizeHvacTemp(value.areaId, std::nullopt);
+ }
+ return {};
case toInt(VehicleProperty::LANE_CENTERING_ASSIST_COMMAND): {
isAdasPropertyAvailableResult =
isAdasPropertyAvailable(toInt(VehicleProperty::LANE_CENTERING_ASSIST_STATE));
@@ -1926,43 +2010,109 @@
mOnPropertySetErrorCallback = std::move(callback);
}
-StatusCode FakeVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) {
- // DefaultVehicleHal makes sure that sampleRate must be within minSampleRate and maxSampleRate.
- // For fake implementation, we would write the same value with a new timestamp into propStore
- // at sample rate.
- std::scoped_lock<std::mutex> lockGuard(mLock);
+StatusCode FakeVehicleHardware::subscribe(SubscribeOptions options) {
+ int32_t propId = options.propId;
+ auto configResult = mServerSidePropStore->getConfig(propId);
+ if (!configResult.ok()) {
+ ALOGE("subscribe: property: %" PRId32 " is not supported", propId);
+ return StatusCode::INVALID_ARG;
+ }
+
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ for (int areaId : options.areaIds) {
+ if (StatusCode status = subscribePropIdAreaIdLocked(propId, areaId, options.sampleRate,
+ options.enableVariableUpdateRate,
+ *configResult.value());
+ status != StatusCode::OK) {
+ return status;
+ }
+ }
+ return StatusCode::OK;
+}
+
+bool FakeVehicleHardware::isVariableUpdateRateSupported(const VehiclePropConfig& vehiclePropConfig,
+ int32_t areaId) {
+ for (size_t i = 0; i < vehiclePropConfig.areaConfigs.size(); i++) {
+ const auto& areaConfig = vehiclePropConfig.areaConfigs[i];
+ if (areaConfig.areaId != areaId) {
+ continue;
+ }
+ if (areaConfig.supportVariableUpdateRate) {
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+StatusCode FakeVehicleHardware::subscribePropIdAreaIdLocked(
+ int32_t propId, int32_t areaId, float sampleRateHz, bool enableVariableUpdateRate,
+ const VehiclePropConfig& vehiclePropConfig) {
+ PropIdAreaId propIdAreaId{
+ .propId = propId,
+ .areaId = areaId,
+ };
+ switch (vehiclePropConfig.changeMode) {
+ case VehiclePropertyChangeMode::STATIC:
+ ALOGW("subscribe to a static property, do nothing.");
+ return StatusCode::OK;
+ case VehiclePropertyChangeMode::ON_CHANGE:
+ mSubOnChangePropIdAreaIds.insert(std::move(propIdAreaId));
+ return StatusCode::OK;
+ case VehiclePropertyChangeMode::CONTINUOUS:
+ if (sampleRateHz == 0.f) {
+ ALOGE("Must not use sample rate 0 for a continuous property");
+ return StatusCode::INTERNAL_ERROR;
+ }
+ if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
+ mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
+ }
+ int64_t intervalInNanos = static_cast<int64_t>(1'000'000'000. / sampleRateHz);
+
+ // For continuous properties, we must generate a new onPropertyChange event
+ // periodically according to the sample rate.
+ auto eventMode = VehiclePropertyStore::EventMode::ALWAYS;
+ if (isVariableUpdateRateSupported(vehiclePropConfig, areaId) &&
+ enableVariableUpdateRate) {
+ eventMode = VehiclePropertyStore::EventMode::ON_VALUE_CHANGE;
+ }
+ auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId,
+ eventMode] {
+ // Refresh the property value. In real implementation, this should poll the latest
+ // value from vehicle bus. Here, we are just refreshing the existing value with a
+ // new timestamp.
+ auto result = getValue(VehiclePropValue{
+ .areaId = areaId,
+ .prop = propId,
+ .value = {},
+ });
+ if (!result.ok()) {
+ // Failed to read current value, skip refreshing.
+ return;
+ }
+ result.value()->timestamp = elapsedRealtimeNano();
+
+ mServerSidePropStore->writeValue(std::move(result.value()), /*updateStatus=*/true,
+ eventMode);
+ });
+ mRecurrentTimer->registerTimerCallback(intervalInNanos, action);
+ mRecurrentActions[propIdAreaId] = action;
+ return StatusCode::OK;
+ }
+}
+
+StatusCode FakeVehicleHardware::unsubscribe(int32_t propId, int32_t areaId) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
PropIdAreaId propIdAreaId{
.propId = propId,
.areaId = areaId,
};
if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
+ mRecurrentActions.erase(propIdAreaId);
}
- if (sampleRate == 0) {
- return StatusCode::OK;
- }
- int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate);
- auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId] {
- // Refresh the property value. In real implementation, this should poll the latest value
- // from vehicle bus. Here, we are just refreshing the existing value with a new timestamp.
- auto result = getValue(VehiclePropValue{
- .areaId = areaId,
- .prop = propId,
- .value = {},
- });
- if (!result.ok()) {
- // Failed to read current value, skip refreshing.
- return;
- }
- result.value()->timestamp = elapsedRealtimeNano();
- // For continuous properties, we must generate a new onPropertyChange event periodically
- // according to the sample rate.
- mServerSidePropStore->writeValue(std::move(result.value()), /*updateStatus=*/true,
- VehiclePropertyStore::EventMode::ALWAYS);
- });
- mRecurrentTimer->registerTimerCallback(interval, action);
- mRecurrentActions[propIdAreaId] = action;
+ mSubOnChangePropIdAreaIds.erase(propIdAreaId);
return StatusCode::OK;
}
@@ -1971,6 +2121,23 @@
return;
}
+ PropIdAreaId propIdAreaId{
+ .propId = value.prop,
+ .areaId = value.areaId,
+ };
+
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ if (mRecurrentActions.find(propIdAreaId) == mRecurrentActions.end() &&
+ mSubOnChangePropIdAreaIds.find(propIdAreaId) == mSubOnChangePropIdAreaIds.end()) {
+ if (FAKE_VEHICLEHARDWARE_DEBUG) {
+ ALOGD("The updated property value: %s is not subscribed, ignore",
+ value.toString().c_str());
+ }
+ return;
+ }
+ }
+
std::vector<VehiclePropValue> updatedValues;
updatedValues.push_back(value);
(*mOnPropertyChangeCallback)(std::move(updatedValues));
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index cf9beee..3b6f717 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -72,6 +72,7 @@
using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateShutdownParam;
@@ -94,6 +95,7 @@
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::IsSubsetOf;
+using ::testing::UnorderedElementsAre;
using ::testing::WhenSortedBy;
using std::chrono::milliseconds;
@@ -149,6 +151,15 @@
mHardware = std::move(hardware);
}
+ static SubscribeOptions newSubscribeOptions(int32_t propId, int32_t areaId,
+ float sampleRateHz) {
+ SubscribeOptions options;
+ options.areaIds = {areaId};
+ options.propId = propId;
+ options.sampleRate = sampleRateHz;
+ return options;
+ }
+
StatusCode setValues(const std::vector<SetValueRequest>& requests) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -336,6 +347,13 @@
return mEventCount[propIdAreaId];
}
+ void subscribe(int32_t propId, int32_t areaId, float sampleRateHz) {
+ ASSERT_EQ(StatusCode::OK,
+ getHardware()->subscribe(newSubscribeOptions(propId, areaId, sampleRateHz)))
+ << "failed to subscribe to propId: " << propId << "areaId: " << areaId
+ << ", sampleRateHz: " << sampleRateHz;
+ }
+
static void addSetValueRequest(std::vector<SetValueRequest>& requests,
std::vector<SetValueResult>& expectedResults, int64_t requestId,
const VehiclePropValue& value, StatusCode expectedStatus) {
@@ -370,24 +388,24 @@
}
std::vector<VehiclePropValue> getTestPropValues() {
- VehiclePropValue fuelCapacity = {
- .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
- .value = {.floatValues = {1.0}},
+ VehiclePropValue oilLevel = {
+ .prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL),
+ .value = {.int32Values = {1}},
};
- VehiclePropValue leftTirePressure = {
- .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ VehiclePropValue leftHvacTemp = {
+ .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_CURRENT),
.value = {.floatValues = {170.0}},
- .areaId = WHEEL_FRONT_LEFT,
+ .areaId = SEAT_1_LEFT,
};
- VehiclePropValue rightTirePressure = {
- .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ VehiclePropValue rightHvacTemp = {
+ .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_CURRENT),
.value = {.floatValues = {180.0}},
- .areaId = WHEEL_FRONT_RIGHT,
+ .areaId = SEAT_1_RIGHT,
};
- return {fuelCapacity, leftTirePressure, rightTirePressure};
+ return {oilLevel, leftHvacTemp, rightHvacTemp};
}
struct PropValueCmp {
@@ -437,6 +455,29 @@
ASSERT_EQ(configs.size(), helper.loadConfigDeclarations().size());
}
+TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs_defaultSupportVUR) {
+ std::vector<VehiclePropConfig> configs = getHardware()->getAllPropertyConfigs();
+
+ for (const auto& config : configs) {
+ bool expectedSupportVUR = true;
+ if (config.prop == toInt(VehicleProperty::VHAL_HEARTBEAT) ||
+ config.prop == toInt(VehicleProperty::CLUSTER_HEARTBEAT)) {
+ expectedSupportVUR = false;
+ }
+ EXPECT_GE(config.areaConfigs.size(), 1u)
+ << "expect at least one area config, including global area config, propId: "
+ << config.prop;
+ if (config.areaConfigs.size() == 0) {
+ continue;
+ }
+ for (const auto& areaConfig : config.areaConfigs) {
+ EXPECT_EQ(areaConfig.supportVariableUpdateRate, expectedSupportVUR)
+ << "unexpected supportVariableUpdateRate for propId: " << config.prop
+ << ", areaId: " << areaConfig.areaId;
+ }
+ }
+}
+
TEST_F(FakeVehicleHardwareTest, testGetDefaultValues) {
std::vector<GetValueRequest> getValueRequests;
std::vector<GetValueResult> expectedGetValueResults;
@@ -559,17 +600,13 @@
ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults));
}
-TEST_F(FakeVehicleHardwareTest, testRegisterOnPropertyChangeEvent) {
- // We have already registered this callback in Setup, here we are registering again.
- auto callback = std::make_unique<IVehicleHardware::PropertyChangeCallback>(
- [this](const std::vector<VehiclePropValue>& values) { onPropertyChangeEvent(values); });
- getHardware()->registerOnPropertyChangeEvent(std::move(callback));
-
+TEST_F(FakeVehicleHardwareTest, testSetValues_getUpdateEvents) {
auto testValues = getTestPropValues();
std::vector<SetValueRequest> requests;
std::vector<SetValueResult> expectedResults;
int64_t requestId = 1;
for (auto& value : testValues) {
+ subscribe(value.prop, value.areaId, /*sampleRateHz=*/0);
addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK);
}
int64_t timestamp = elapsedRealtimeNano();
@@ -1624,27 +1661,30 @@
return info.param.name;
});
-TEST_F(FakeVehicleHardwareTest, testSetWaitForVhalAfterCarServiceCrash) {
- int32_t propId = toInt(VehicleProperty::AP_POWER_STATE_REPORT);
+TEST_F(FakeVehicleHardwareTest, testSetWaitForVhal_alwaysTriggerEvents) {
+ int32_t powerReq = toInt(VehicleProperty::AP_POWER_STATE_REQ);
+ subscribe(powerReq, /*areaId*/ 0, /*sampleRateHz*/ 0);
+
+ int32_t powerReport = toInt(VehicleProperty::AP_POWER_STATE_REPORT);
VehiclePropValue request = VehiclePropValue{
- .prop = propId,
+ .prop = powerReport,
.value.int32Values = {toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL)},
};
- ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << propId;
+ ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << powerReport;
// Clear existing events.
clearChangedProperties();
// Simulate a Car Service crash, Car Service would restart and send the message again.
- ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << propId;
+ ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << powerReport;
std::vector<VehiclePropValue> events = getChangedProperties();
// Even though the state is already ON, we should receive another ON event.
- ASSERT_EQ(events.size(), 1u);
+ ASSERT_EQ(events.size(), 1u) << "failed to receive on-change events AP_POWER_STATE_REQ ON";
// Erase the timestamp for comparison.
events[0].timestamp = 0;
auto expectedValue = VehiclePropValue{
- .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ),
+ .prop = powerReq,
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values = {toInt(VehicleApPowerStateReq::ON), 0},
};
@@ -1849,6 +1889,101 @@
}
}
+TEST_F(FakeVehicleHardwareTest, testHvacDualOnSynchronizesTemp) {
+ auto hvacDualOnConfig = std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_DUAL_ON)));
+ auto hvacTemperatureSetConfig =
+ std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_TEMPERATURE_SET)));
+ EXPECT_NE(hvacDualOnConfig, nullptr);
+ EXPECT_NE(hvacTemperatureSetConfig, nullptr);
+ for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) {
+ int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId;
+ subscribe(toInt(VehicleProperty::HVAC_TEMPERATURE_SET), hvacTemperatureSetAreaId,
+ /*sampleRateHz*/ 0);
+ }
+ for (auto& hvacDualOnConfig : hvacDualOnConfig->areaConfigs) {
+ int32_t hvacDualOnAreaId = hvacDualOnConfig.areaId;
+ subscribe(toInt(VehicleProperty::HVAC_DUAL_ON), hvacDualOnAreaId, /*sampleRateHz*/ 0);
+ StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_DUAL_ON),
+ .areaId = hvacDualOnAreaId,
+ .value.int32Values = {1}});
+ EXPECT_EQ(status, StatusCode::OK);
+
+ // Verify there's an event for all HVAC_TEMPERATURE_SET
+ // area IDs covered by the HVAC_DUAL_ON area ID
+ auto events = getChangedProperties();
+ std::unordered_set<float> temperatureValues;
+ for (const auto& event : events) {
+ // Ignore HVAC_DUAL_ON event
+ if (event.prop == toInt(VehicleProperty::HVAC_DUAL_ON)) {
+ continue;
+ }
+ EXPECT_EQ(event.prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+ EXPECT_EQ((hvacDualOnAreaId & event.areaId), event.areaId);
+ EXPECT_EQ(1u, event.value.floatValues.size());
+ temperatureValues.insert(event.value.floatValues[0]);
+ }
+ // Verify that the temperature value is the same for all events
+ // Ie the temperature in all area IDs are synchronized
+ EXPECT_EQ(1u, temperatureValues.size());
+ clearChangedProperties();
+
+ // Verify when any HVAC_TEMPERATURE_SET area ID is changed all
+ // area IDs covered by the HVAC_DUAL_ON area ID are also changed
+ for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) {
+ int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId;
+ if ((hvacDualOnAreaId & hvacTemperatureSetAreaId) != hvacTemperatureSetAreaId) {
+ continue;
+ }
+ float expectedValue = 25;
+ status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
+ .areaId = hvacTemperatureSetAreaId,
+ .value.floatValues = {expectedValue}});
+ EXPECT_EQ(status, StatusCode::OK);
+ events = getChangedProperties();
+ for (const auto& event : events) {
+ EXPECT_EQ(event.prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+ EXPECT_EQ(1u, event.value.floatValues.size());
+ EXPECT_EQ(expectedValue, event.value.floatValues[0]);
+ }
+ clearChangedProperties();
+ }
+
+ status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_DUAL_ON),
+ .areaId = hvacDualOnAreaId,
+ .value.int32Values = {0}});
+ EXPECT_EQ(status, StatusCode::OK);
+
+ // When HVAC_DUAL_ON is disabled, there should be no events created
+ // for HVAC_TEMPERATURE_SET ie no temperature synchronization.
+ events = getChangedProperties();
+ EXPECT_EQ(1u, events.size());
+ EXPECT_EQ(events[0].prop, toInt(VehicleProperty::HVAC_DUAL_ON));
+ EXPECT_EQ(events[0].areaId, hvacDualOnAreaId);
+ clearChangedProperties();
+
+ // Verify when any HVAC_TEMPERATURE_SET area ID is
+ // changed other area IDs do not change.
+ for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) {
+ int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId;
+ if ((hvacDualOnAreaId & hvacTemperatureSetAreaId) != hvacTemperatureSetAreaId) {
+ continue;
+ }
+ float expectedValue = 24;
+ status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
+ .areaId = hvacTemperatureSetAreaId,
+ .value.floatValues = {expectedValue}});
+ EXPECT_EQ(status, StatusCode::OK);
+ events = getChangedProperties();
+ EXPECT_EQ(1u, events.size());
+ EXPECT_EQ(events[0].prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+ EXPECT_EQ(events[0].areaId, hvacTemperatureSetAreaId);
+ EXPECT_EQ(1u, events[0].value.floatValues.size());
+ EXPECT_EQ(expectedValue, events[0].value.floatValues[0]);
+ clearChangedProperties();
+ }
+ }
+}
+
TEST_F(FakeVehicleHardwareTest, testGetAdasPropNotAvailable) {
std::unordered_map<int32_t, std::vector<int32_t>> adasEnabledPropToDependentProps = {
{
@@ -2015,6 +2150,22 @@
},
},
};
+
+ // First subscribe to all the properties that we will change.
+ for (auto& enabledToErrorStateProps : adasEnabledPropToAdasPropWithErrorState) {
+ std::unordered_set<int32_t> expectedChangedPropIds(enabledToErrorStateProps.second.begin(),
+ enabledToErrorStateProps.second.end());
+ expectedChangedPropIds.insert(enabledToErrorStateProps.first);
+
+ for (int32_t propId : expectedChangedPropIds) {
+ int32_t areaId = 0;
+ if (propId == toInt(VehicleProperty::BLIND_SPOT_WARNING_STATE)) {
+ areaId = toInt(VehicleAreaMirror::DRIVER_LEFT);
+ }
+ subscribe(propId, areaId, /*sampleRateHz*/ 0);
+ }
+ }
+
for (auto& enabledToErrorStateProps : adasEnabledPropToAdasPropWithErrorState) {
int32_t adasEnabledPropertyId = enabledToErrorStateProps.first;
StatusCode status =
@@ -2095,9 +2246,16 @@
}
TEST_F(FakeVehicleHardwareTest, testSwitchUser) {
+ SubscribeOptions options;
+ int32_t propSwitchUser = toInt(VehicleProperty::SWITCH_USER);
+ options.propId = propSwitchUser;
+ options.areaIds = {0, 1};
+ ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options))
+ << "failed to subscribe to propId: " << propSwitchUser;
+
// This is the same example as used in User HAL Emulation doc.
VehiclePropValue valueToSet = {
- .prop = toInt(VehicleProperty::SWITCH_USER),
+ .prop = propSwitchUser,
.areaId = 1,
.value.int32Values = {666, 3, 2},
};
@@ -2108,7 +2266,7 @@
// Simulate a request from Android side.
VehiclePropValue switchUserRequest = {
- .prop = toInt(VehicleProperty::SWITCH_USER),
+ .prop = propSwitchUser,
.areaId = 0,
.value.int32Values = {666, 3},
};
@@ -2138,7 +2296,7 @@
events[0].timestamp = 0;
auto expectedValue = VehiclePropValue{
.areaId = 0,
- .prop = toInt(VehicleProperty::SWITCH_USER),
+ .prop = propSwitchUser,
.value.int32Values =
{
// Request ID
@@ -2153,6 +2311,13 @@
}
TEST_F(FakeVehicleHardwareTest, testCreateUser) {
+ SubscribeOptions options;
+ int32_t propCreateUser = toInt(VehicleProperty::CREATE_USER);
+ options.propId = propCreateUser;
+ options.areaIds = {0, 1};
+ ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options))
+ << "failed to subscribe to propId: " << propCreateUser;
+
// This is the same example as used in User HAL Emulation doc.
VehiclePropValue valueToSet = {
.prop = toInt(VehicleProperty::CREATE_USER),
@@ -2166,7 +2331,7 @@
// Simulate a request from Android side.
VehiclePropValue createUserRequest = {
- .prop = toInt(VehicleProperty::CREATE_USER),
+ .prop = propCreateUser,
.areaId = 0,
.value.int32Values = {666},
};
@@ -2195,7 +2360,7 @@
events[0].timestamp = 0;
auto expectedValue = VehiclePropValue{
.areaId = 0,
- .prop = toInt(VehicleProperty::CREATE_USER),
+ .prop = propCreateUser,
.value.int32Values =
{
// Request ID
@@ -2208,9 +2373,16 @@
}
TEST_F(FakeVehicleHardwareTest, testInitialUserInfo) {
+ SubscribeOptions options;
+ int32_t propInitialUserInfo = toInt(VehicleProperty::INITIAL_USER_INFO);
+ options.propId = propInitialUserInfo;
+ options.areaIds = {0, 1};
+ ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options))
+ << "failed to subscribe to propId: " << propInitialUserInfo;
+
// This is the same example as used in User HAL Emulation doc.
VehiclePropValue valueToSet = {
- .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+ .prop = propInitialUserInfo,
.areaId = 1,
.value.int32Values = {666, 1, 11},
};
@@ -2221,7 +2393,7 @@
// Simulate a request from Android side.
VehiclePropValue initialUserInfoRequest = {
- .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+ .prop = propInitialUserInfo,
.areaId = 0,
.value.int32Values = {3},
};
@@ -2238,7 +2410,7 @@
events[0].timestamp = 0;
auto expectedValue = VehiclePropValue{
.areaId = 0,
- .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+ .prop = propInitialUserInfo,
.value.int32Values = {3, 1, 11},
};
EXPECT_EQ(events[0], expectedValue);
@@ -2253,7 +2425,7 @@
events[0].timestamp = 0;
expectedValue = VehiclePropValue{
.areaId = 0,
- .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+ .prop = propInitialUserInfo,
.value.int32Values =
{
// Request ID
@@ -2395,13 +2567,14 @@
}
TEST_F(FakeVehicleHardwareTest, testDumpInjectEvent) {
- int32_t prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+ int32_t prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL);
std::string propIdStr = std::to_string(prop);
+ subscribe(prop, /*areaId*/ 0, /*sampleRateHz*/ 0);
+
int64_t timestamp = elapsedRealtimeNano();
- // Inject an event with float value 123.4 and timestamp.
DumpResult result = getHardware()->dump(
- {"--inject-event", propIdStr, "-f", "123.4", "-t", std::to_string(timestamp)});
+ {"--inject-event", propIdStr, "-i", "1234", "-t", std::to_string(timestamp)});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer,
@@ -2412,7 +2585,7 @@
ASSERT_EQ(events.size(), 1u);
auto event = events[0];
ASSERT_EQ(event.timestamp, timestamp);
- ASSERT_EQ(event.value.floatValues, std::vector<float>({123.4}));
+ ASSERT_EQ(event.value.int32Values, std::vector<int32_t>({1234}));
}
TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
@@ -2755,9 +2928,13 @@
});
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataLinear) {
- // Start a fake linear data generator for vehicle speed at 0.1s interval.
+ // Start a fake linear data generator for engine oil level at 0.1s interval.
// range: 0 - 100, current value: 30, step: 20.
- std::string propIdString = StringPrintf("%d", toInt(VehicleProperty::PERF_VEHICLE_SPEED));
+ int32_t prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL);
+
+ subscribe(prop, /*areaId*/ 0, /*sampleRateHz*/ 0);
+
+ std::string propIdString = StringPrintf("%d", prop);
std::vector<std::string> options = {"--genfakedata", "--startlinear", propIdString,
/*middleValue=*/"50",
/*currentValue=*/"30",
@@ -2770,15 +2947,14 @@
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
- ASSERT_TRUE(waitForChangedProperties(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0, /*count=*/5,
- milliseconds(1000)))
+ ASSERT_TRUE(waitForChangedProperties(prop, 0, /*count=*/5, milliseconds(1000)))
<< "not enough events generated for linear data generator";
int32_t value = 30;
auto events = getChangedProperties();
for (size_t i = 0; i < 5; i++) {
- ASSERT_EQ(1u, events[i].value.floatValues.size());
- EXPECT_EQ(static_cast<float>(value), events[i].value.floatValues[0]);
+ ASSERT_EQ(1u, events[i].value.int32Values.size());
+ EXPECT_EQ(value, events[i].value.int32Values[0]);
value = (value + 20) % 100;
}
@@ -2794,7 +2970,7 @@
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// There should be no new events generated.
- EXPECT_EQ(0u, getEventCount(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0));
+ EXPECT_EQ(0u, getEventCount(prop, 0));
}
std::string getTestFilePath(const char* filename) {
@@ -2803,6 +2979,8 @@
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) {
+ subscribe(toInt(VehicleProperty::GEAR_SELECTION), /*areaId*/ 0, /*sampleRateHz*/ 0);
+
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("prop.json"), "2"};
@@ -2829,6 +3007,8 @@
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonByContent) {
+ subscribe(toInt(VehicleProperty::GEAR_SELECTION), /*areaId*/ 0, /*sampleRateHz*/ 0);
+
std::vector<std::string> options = {
"--genfakedata", "--startjson", "--content",
"[{\"timestamp\":1000000,\"areaId\":0,\"value\":8,\"prop\":289408000}]", "1"};
@@ -2903,8 +3083,11 @@
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyPress) {
+ int32_t propHwKeyInput = toInt(VehicleProperty::HW_KEY_INPUT);
std::vector<std::string> options = {"--genfakedata", "--keypress", "1", "2"};
+ subscribe(propHwKeyInput, /*areaId*/ 0, /*sampleRateHz*/ 0);
+
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
@@ -2912,8 +3095,8 @@
auto events = getChangedProperties();
ASSERT_EQ(2u, events.size());
- EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[0].prop);
- EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[1].prop);
+ EXPECT_EQ(propHwKeyInput, events[0].prop);
+ EXPECT_EQ(propHwKeyInput, events[1].prop);
ASSERT_EQ(3u, events[0].value.int32Values.size());
ASSERT_EQ(3u, events[1].value.int32Values.size());
EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_DOWN), events[0].value.int32Values[0]);
@@ -2925,8 +3108,11 @@
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyInputV2) {
+ int32_t propHwKeyInputV2 = toInt(VehicleProperty::HW_KEY_INPUT_V2);
std::vector<std::string> options = {"--genfakedata", "--keyinputv2", "1", "2", "3", "4", "5"};
+ subscribe(propHwKeyInputV2, /*areaId*/ 1, /*sampleRateHz*/ 0);
+
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
@@ -2944,6 +3130,7 @@
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataMotionInput) {
+ int32_t propHwMotionInput = toInt(VehicleProperty::HW_MOTION_INPUT);
std::vector<std::string> options = {"--genfakedata",
"--motioninput",
"1",
@@ -2966,6 +3153,8 @@
"65.5",
"76.6"};
+ subscribe(propHwMotionInput, /*areaId*/ 1, /*sampleRateHz*/ 0);
+
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
@@ -2973,7 +3162,7 @@
auto events = getChangedProperties();
ASSERT_EQ(1u, events.size());
- EXPECT_EQ(toInt(VehicleProperty::HW_MOTION_INPUT), events[0].prop);
+ EXPECT_EQ(propHwMotionInput, events[0].prop);
ASSERT_EQ(9u, events[0].value.int32Values.size());
EXPECT_EQ(2, events[0].value.int32Values[0]);
EXPECT_EQ(3, events[0].value.int32Values[1]);
@@ -3014,23 +3203,27 @@
ASSERT_EQ(result.value().value.byteValues, std::vector<uint8_t>({0x04, 0x03, 0x02, 0x01}));
}
-TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) {
+TEST_F(FakeVehicleHardwareTest, testSubscribeUnsubscribe_continuous) {
int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
int32_t propSteering = toInt(VehicleProperty::PERF_STEERING_ANGLE);
int32_t areaId = 0;
- getHardware()->updateSampleRate(propSpeed, areaId, 5);
+
+ auto status = getHardware()->subscribe(newSubscribeOptions(propSpeed, areaId, 5));
+ ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe";
ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500)))
<< "not enough events generated for speed";
- getHardware()->updateSampleRate(propSteering, areaId, 10);
+ status = getHardware()->subscribe(newSubscribeOptions(propSteering, areaId, 10));
+ ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe";
ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/10, milliseconds(1500)))
<< "not enough events generated for steering";
int64_t timestamp = elapsedRealtimeNano();
// Disable refreshing for propSpeed.
- getHardware()->updateSampleRate(propSpeed, areaId, 0);
+ status = getHardware()->unsubscribe(propSpeed, areaId);
+ ASSERT_EQ(status, StatusCode::OK) << "failed to unsubscribe";
clearChangedProperties();
ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500)))
@@ -3043,12 +3236,99 @@
}
}
+TEST_F(FakeVehicleHardwareTest, testSubscribe_enableVUR) {
+ int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+ int32_t areaId = 0;
+ SubscribeOptions options;
+ options.propId = propSpeed;
+ options.areaIds = {areaId};
+ options.enableVariableUpdateRate = true;
+ options.sampleRate = 5;
+ int64_t timestamp = elapsedRealtimeNano();
+
+ auto status = getHardware()->subscribe(options);
+ ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe";
+
+ status = setValue({
+ .prop = propSpeed,
+ .areaId = 0,
+ .value.floatValues = {1.1f},
+ });
+ ASSERT_EQ(status, StatusCode::OK) << "failed to set speed";
+
+ status = setValue({
+ .prop = propSpeed,
+ .areaId = 0,
+ .value.floatValues = {1.2f},
+ });
+ ASSERT_EQ(status, StatusCode::OK) << "failed to set speed";
+
+ ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/2, milliseconds(100)))
+ << "not enough events generated for speed";
+ auto updatedValues = getChangedProperties();
+ std::unordered_set<float> gotValues;
+ for (auto& value : updatedValues) {
+ EXPECT_GE(value.timestamp, timestamp) << "timestamp must be updated";
+ EXPECT_EQ(value.prop, propSpeed) << "propId must be correct";
+ EXPECT_EQ(value.areaId, areaId) << "areaId must be correct";
+ gotValues.insert(value.value.floatValues[0]);
+ }
+ EXPECT_THAT(gotValues, UnorderedElementsAre(1.1f, 1.2f))
+ << "must only receive property event for changed value";
+}
+
+TEST_F(FakeVehicleHardwareTest, testSubscribeUnusubscribe_onChange) {
+ int32_t propHvac = toInt(VehicleProperty::HVAC_TEMPERATURE_SET);
+ int32_t areaId = SEAT_1_LEFT;
+
+ auto status = getHardware()->subscribe(newSubscribeOptions(propHvac, areaId, 0));
+ ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe";
+
+ status = setValue({
+ .prop = propHvac,
+ .areaId = areaId,
+ .value.floatValues = {20.0f},
+ });
+ ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value";
+
+ ASSERT_TRUE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100)))
+ << "not enough on change events generated for hvac";
+ clearChangedProperties();
+
+ status = setValue({
+ .prop = propHvac,
+ .areaId = areaId,
+ .value.floatValues = {21.0f},
+ });
+ ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value";
+
+ ASSERT_TRUE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100)))
+ << "not enough on change events generated for hvac";
+ clearChangedProperties();
+
+ status = getHardware()->unsubscribe(propHvac, areaId);
+ ASSERT_EQ(status, StatusCode::OK);
+
+ status = setValue({
+ .prop = propHvac,
+ .areaId = areaId,
+ .value.floatValues = {22.0f},
+ });
+ ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value";
+
+ ASSERT_FALSE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100)))
+ << "must not receive on change events if the propId, areaId is unsubscribed";
+}
+
TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) {
float CELSIUS = static_cast<float>(toInt(VehicleUnit::CELSIUS));
float FAHRENHEIT = static_cast<float>(toInt(VehicleUnit::FAHRENHEIT));
+ int32_t propHvacTempValueSuggest = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION);
+
+ subscribe(propHvacTempValueSuggest, HVAC_ALL, /*sampleRateHz*/ 0);
VehiclePropValue floatArraySizeFour = {
- .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {0, CELSIUS, 0, 0},
};
@@ -3056,14 +3336,14 @@
EXPECT_EQ(status, StatusCode::OK);
VehiclePropValue floatArraySizeZero = {
- .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
};
status = setValue(floatArraySizeZero);
EXPECT_EQ(status, StatusCode::INVALID_ARG);
VehiclePropValue floatArraySizeFive = {
- .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {0, CELSIUS, 0, 0, 0},
};
@@ -3071,7 +3351,7 @@
EXPECT_EQ(status, StatusCode::INVALID_ARG);
VehiclePropValue invalidUnit = {
- .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {0, 0, 0, 0},
};
@@ -3102,9 +3382,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius, CELSIUS, 0, 0},
},
@@ -3112,9 +3390,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius, CELSIUS,
minTempInCelsius,
@@ -3127,9 +3403,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
0, 0},
@@ -3138,9 +3412,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
minTempInCelsius,
@@ -3153,9 +3425,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInCelsius, CELSIUS, 0, 0},
},
@@ -3163,9 +3433,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInCelsius, CELSIUS,
maxTempInCelsius,
@@ -3178,9 +3446,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
0, 0},
@@ -3189,9 +3455,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
maxTempInCelsius,
@@ -3204,9 +3468,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius - 1, CELSIUS, 0,
0},
@@ -3215,9 +3477,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius - 1, CELSIUS,
minTempInCelsius,
@@ -3230,9 +3490,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit - 1,
FAHRENHEIT, 0, 0},
@@ -3241,9 +3499,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit - 1,
FAHRENHEIT, minTempInCelsius,
@@ -3256,9 +3512,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInCelsius + 1, CELSIUS, 0,
0},
@@ -3267,9 +3521,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInCelsius + 1, CELSIUS,
maxTempInCelsius,
@@ -3282,9 +3534,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInFahrenheit + 1,
FAHRENHEIT, 0, 0},
@@ -3293,9 +3543,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInFahrenheit + 1,
FAHRENHEIT, maxTempInCelsius,
@@ -3308,9 +3556,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius +
incrementInCelsius * 2.5f,
@@ -3320,9 +3566,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues =
{minTempInCelsius + incrementInCelsius * 2.5f,
@@ -3338,9 +3582,7 @@
.valuesToSet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit +
incrementInFahrenheit *
@@ -3351,9 +3593,7 @@
.expectedValuesToGet =
{
VehiclePropValue{
- .prop = toInt(
- VehicleProperty::
- HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .prop = propHvacTempValueSuggest,
.areaId = HVAC_ALL,
.value.floatValues =
{minTempInFahrenheit +
diff --git a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/src/ProtoMessageConverter.cpp b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/src/ProtoMessageConverter.cpp
index 6b789bb..491aa10 100644
--- a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/src/ProtoMessageConverter.cpp
+++ b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/src/ProtoMessageConverter.cpp
@@ -78,6 +78,7 @@
protoACfg->add_supported_enum_values(supportedEnumValue);
}
}
+ protoACfg->set_support_variable_update_rate(areaConfig.supportVariableUpdateRate);
}
}
@@ -100,9 +101,14 @@
.maxInt64Value = protoAcfg.max_int64_value(),
.minFloatValue = protoAcfg.min_float_value(),
.maxFloatValue = protoAcfg.max_float_value(),
+ .supportVariableUpdateRate = protoAcfg.support_variable_update_rate(),
};
- COPY_PROTOBUF_VEC_TO_VHAL_TYPE(protoAcfg, supported_enum_values, (&vehicleAreaConfig),
- supportedEnumValues.value());
+ if (protoAcfg.supported_enum_values().size() != 0) {
+ vehicleAreaConfig.supportedEnumValues = std::vector<int64_t>();
+ COPY_PROTOBUF_VEC_TO_VHAL_TYPE(protoAcfg, supported_enum_values, (&vehicleAreaConfig),
+ supportedEnumValues.value());
+ }
+
return vehicleAreaConfig;
};
CAST_COPY_PROTOBUF_VEC_TO_VHAL_TYPE(in, area_configs, out, areaConfigs, cast_to_acfg);
diff --git a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
index 3fecbd9..f49d91b 100644
--- a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
@@ -82,35 +82,6 @@
const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>&
requests) const = 0;
- // Update the sampling rate for the specified property and the specified areaId (0 for global
- // property) if server supports it. The property must be a continuous property.
- // {@code sampleRate} means that for this specific property, the server must generate at least
- // this many OnPropertyChange events per seconds.
- // A sampleRate of 0 means the property is no longer subscribed and server does not need to
- // generate any onPropertyEvent for this property.
- // This would be called if sample rate is updated for a subscriber, a new subscriber is added
- // or an existing subscriber is removed. For example:
- // 1. We have no subscriber for speed.
- // 2. A new subscriber is subscribing speed for 10 times/s, updsateSampleRate would be called
- // with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
- // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
- // times/sec, updateSampleRate would not be called.
- // 4. The initial subscriber is removed, updateSampleRate would be called with sampleRate as
- // 5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
- // speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
- // the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
- // events.
- // 5. The second subscriber is removed, updateSampleRate would be called with sampleRate as 0.
- // The impl can optionally disable the polling for vehicle speed.
- //
- // 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;
- }
-
// Dump debug information in the server.
virtual DumpResult dump(const std::vector<std::string>& options) = 0;
@@ -145,6 +116,111 @@
// By default batching is disabled.
return std::chrono::nanoseconds(0);
}
+
+ // A [propId, areaId] is newly subscribed or the subscribe options are changed.
+ //
+ // The subscribe options contain sample rate in Hz or enable/disable variable update rate.
+ //
+ // For continuous properties:
+ //
+ // The sample rate is never 0 and indicates the desired polling rate for this property. The
+ // sample rate is guaranteed to be within supported {@code minSampleRate} and
+ // {@code maxSampleRate} as specified in {@code VehiclePropConfig}.
+ //
+ // If the specified sample rate is not supported, e.g. vehicle bus only supports 5hz and 10hz
+ // polling rate but the sample rate is 8hz, impl must choose the higher polling rate (10hz).
+ //
+ // Whether variable update rate is enabled is specified by {@code enableVariableUpdateRate} in
+ // {@code SubscribeOptions}. If variable update rate is not supported for the
+ // [propId, areaId], impl must ignore this option and always treat it as disabled.
+ //
+ // If variable update rate is disabled/not supported, impl must report all the property events
+ // for this [propId, areaId] through {@code propertyChangeCallback} according to the sample
+ // rate. E.g. a sample rate of 10hz must generate at least 10 property change events per second.
+ //
+ // If variable update rate is enabled AND supported, impl must only report property events
+ // when the [propId, areaId]'s value or status changes (a.k.a same as on-change property).
+ // The sample rate still guides the polling rate, but duplicate property events must be dropped
+ // and not reported via {@code propertyChangeCallback}.
+ //
+ // Async property set error events are not affected by variable update rate and must always
+ // be reported.
+ //
+ // If the impl is always polling at {@code maxSampleRate} for all continuous [propId, areaId]s,
+ // and do not support variable update rate for any [propId, areaId], then this function can be a
+ // no-op.
+ //
+ // For on-change properties:
+ //
+ // The sample rate is always 0 and must be ignored. If the impl is always subscribing to all
+ // on-change properties, then this function can be no-op.
+ //
+ // For all properties:
+ //
+ // It is recommended to only deliver the subscribed property events to DefaultVehicleHal to
+ // improve performance. However, even if unsubscribed property events are delivered, they
+ // will be filtered out by DefaultVehicleHal.
+ //
+ // A subscription from VHAL client might not necessarily trigger this function.
+ // DefaultVehicleHal will aggregate all the subscriptions from all the clients and notify
+ // IVehicleHardware if new subscriptions are required or subscribe options are updated.
+ //
+ // For example:
+ // 1. VHAL initially have no subscriber for speed.
+ // 2. A new subscriber is subscribing speed for 10 times/s, 'subscribe' is called
+ // with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
+ // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
+ // times/sec, 'subscribe' is not called.
+ // 4. The initial subscriber is removed, 'subscribe' is called with sampleRate as
+ // 5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
+ // speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
+ // the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
+ // events.
+ // 5. The second subscriber is removed, 'unsubscribe' is called.
+ // The impl can optionally disable the polling for vehicle speed.
+ //
+ virtual aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
+ [[maybe_unused]] aidl::android::hardware::automotive::vehicle::SubscribeOptions
+ options) {
+ return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+ }
+
+ // A [propId, areaId] is unsubscribed. This applies for both continuous or on-change property.
+ virtual aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(
+ [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId) {
+ return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+ }
+
+ // This function is deprecated, subscribe/unsubscribe should be used instead.
+ //
+ // Update the sampling rate for the specified property and the specified areaId (0 for global
+ // property) if server supports it. The property must be a continuous property.
+ // {@code sampleRate} means that for this specific property, the server must generate at least
+ // this many OnPropertyChange events per seconds.
+ // A sampleRate of 0 means the property is no longer subscribed and server does not need to
+ // generate any onPropertyEvent for this property.
+ // This would be called if sample rate is updated for a subscriber, a new subscriber is added
+ // or an existing subscriber is removed. For example:
+ // 1. We have no subscriber for speed.
+ // 2. A new subscriber is subscribing speed for 10 times/s, updateSampleRate would be called
+ // with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
+ // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
+ // times/sec, updateSampleRate would not be called.
+ // 4. The initial subscriber is removed, updateSampleRate would be called with sampleRate as
+ // 5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
+ // speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
+ // the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
+ // events.
+ // 5. The second subscriber is removed, updateSampleRate would be called with sampleRate as 0.
+ // The impl can optionally disable the polling for vehicle speed.
+ //
+ // 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;
+ }
};
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehicleAreaConfig.proto b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehicleAreaConfig.proto
index 04b7dd4..8093658 100644
--- a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehicleAreaConfig.proto
+++ b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehicleAreaConfig.proto
@@ -44,4 +44,5 @@
*/
repeated int64 supported_enum_values = 8;
int32 access = 9;
+ bool support_variable_update_rate = 10;
};
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
index 3d25cd3..b74dff5 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
@@ -92,7 +92,7 @@
// used as the key.
void registerProperty(
const aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config,
- TokenFunction tokenFunc = nullptr);
+ TokenFunction tokenFunc = nullptr) EXCLUDES(mLock);
// Stores provided value. Returns error if config wasn't registered. If 'updateStatus' is
// true, the 'status' in 'propValue' would be stored. Otherwise, if this is a new value,
@@ -102,44 +102,47 @@
// 'EventMode' controls whether the 'OnValueChangeCallback' will be called for this operation.
VhalResult<void> writeValue(VehiclePropValuePool::RecyclableType propValue,
bool updateStatus = false,
- EventMode mode = EventMode::ON_VALUE_CHANGE);
+ EventMode mode = EventMode::ON_VALUE_CHANGE) EXCLUDES(mLock);
// Remove a given property value from the property store. The 'propValue' would be used to
// generate the key for the value to remove.
void removeValue(
- const aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
+ const aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue)
+ EXCLUDES(mLock);
// Remove all the values for the property.
- void removeValuesForProperty(int32_t propId);
+ void removeValuesForProperty(int32_t propId) EXCLUDES(mLock);
// Read all the stored values.
- std::vector<VehiclePropValuePool::RecyclableType> readAllValues() const;
+ std::vector<VehiclePropValuePool::RecyclableType> readAllValues() const EXCLUDES(mLock);
// Read all the values for the property.
- ValuesResultType readValuesForProperty(int32_t propId) const;
+ ValuesResultType readValuesForProperty(int32_t propId) const EXCLUDES(mLock);
// Read the value for the requested property. Returns {@code StatusCode::NOT_AVAILABLE} if the
// value has not been set yet. Returns {@code StatusCode::INVALID_ARG} if the property is
// not configured.
ValueResultType readValue(
- const aidl::android::hardware::automotive::vehicle::VehiclePropValue& request) const;
+ const aidl::android::hardware::automotive::vehicle::VehiclePropValue& request) const
+ EXCLUDES(mLock);
// Read the value for the requested property. Returns {@code StatusCode::NOT_AVAILABLE} if the
// value has not been set yet. Returns {@code StatusCode::INVALID_ARG} if the property is
// not configured.
- ValueResultType readValue(int32_t prop, int32_t area = 0, int64_t token = 0) const;
+ ValueResultType readValue(int32_t prop, int32_t area = 0, int64_t token = 0) const
+ EXCLUDES(mLock);
// Get all property configs.
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig> getAllConfigs()
- const;
+ const EXCLUDES(mLock);
// Get the property config for the requested property.
android::base::Result<const aidl::android::hardware::automotive::vehicle::VehiclePropConfig*,
VhalError>
- getConfig(int32_t propId) const;
+ getConfig(int32_t propId) const EXCLUDES(mLock);
// Set a callback that would be called when a property value has been updated.
- void setOnValueChangeCallback(const OnValueChangeCallback& callback);
+ void setOnValueChangeCallback(const OnValueChangeCallback& callback) EXCLUDES(mLock);
inline std::shared_ptr<VehiclePropValuePool> getValuePool() { return mValuePool; }
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
index c94bad6..546421e 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
@@ -329,6 +329,11 @@
}
};
+inline std::string propIdToString(int32_t propId) {
+ return toString(
+ static_cast<aidl::android::hardware::automotive::vehicle::VehicleProperty>(propId));
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
index 646dc0e..3fd2aa8 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
@@ -108,51 +108,62 @@
VhalResult<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableType propValue,
bool updateStatus,
VehiclePropertyStore::EventMode eventMode) {
- std::scoped_lock<std::mutex> g(mLock);
-
- int32_t propId = propValue->prop;
-
- VehiclePropertyStore::Record* record = getRecordLocked(propId);
- if (record == nullptr) {
- return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
- }
-
- if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) {
- return StatusError(StatusCode::INVALID_ARG)
- << "no config for property: " << propId << " area: " << propValue->areaId;
- }
-
- VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record);
bool valueUpdated = true;
- if (auto it = record->values.find(recId); it != record->values.end()) {
- const VehiclePropValue* valueToUpdate = it->second.get();
- int64_t oldTimestamp = valueToUpdate->timestamp;
- VehiclePropertyStatus oldStatus = valueToUpdate->status;
- // propValue is outdated and drops it.
- if (oldTimestamp > propValue->timestamp) {
+ VehiclePropValue updatedValue;
+ OnValueChangeCallback onValueChangeCallback = nullptr;
+ {
+ std::scoped_lock<std::mutex> g(mLock);
+
+ int32_t propId = propValue->prop;
+
+ VehiclePropertyStore::Record* record = getRecordLocked(propId);
+ if (record == nullptr) {
return StatusError(StatusCode::INVALID_ARG)
- << "outdated timestamp: " << propValue->timestamp;
- }
- if (!updateStatus) {
- propValue->status = oldStatus;
+ << "property: " << propId << " not registered";
}
- valueUpdated = (valueToUpdate->value != propValue->value ||
- valueToUpdate->status != propValue->status ||
- valueToUpdate->prop != propValue->prop ||
- valueToUpdate->areaId != propValue->areaId);
- } else if (!updateStatus) {
- propValue->status = VehiclePropertyStatus::AVAILABLE;
+ if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) {
+ return StatusError(StatusCode::INVALID_ARG)
+ << "no config for property: " << propId << " area ID: " << propValue->areaId;
+ }
+
+ VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record);
+ if (auto it = record->values.find(recId); it != record->values.end()) {
+ const VehiclePropValue* valueToUpdate = it->second.get();
+ int64_t oldTimestampNanos = valueToUpdate->timestamp;
+ VehiclePropertyStatus oldStatus = valueToUpdate->status;
+ // propValue is outdated and drops it.
+ if (oldTimestampNanos > propValue->timestamp) {
+ return StatusError(StatusCode::INVALID_ARG)
+ << "outdated timestampNanos: " << propValue->timestamp;
+ }
+ if (!updateStatus) {
+ propValue->status = oldStatus;
+ }
+
+ valueUpdated = (valueToUpdate->value != propValue->value ||
+ valueToUpdate->status != propValue->status ||
+ valueToUpdate->prop != propValue->prop ||
+ valueToUpdate->areaId != propValue->areaId);
+ } else if (!updateStatus) {
+ propValue->status = VehiclePropertyStatus::AVAILABLE;
+ }
+
+ record->values[recId] = std::move(propValue);
+
+ if (eventMode == EventMode::NEVER) {
+ return {};
+ }
+ updatedValue = *(record->values[recId]);
+ if (mOnValueChangeCallback == nullptr) {
+ return {};
+ }
+ onValueChangeCallback = mOnValueChangeCallback;
}
- record->values[recId] = std::move(propValue);
-
- if (eventMode == EventMode::NEVER) {
- return {};
- }
-
- if ((eventMode == EventMode::ALWAYS || valueUpdated) && mOnValueChangeCallback != nullptr) {
- mOnValueChangeCallback(*(record->values[recId]));
+ // Invoke the callback outside the lock to prevent dead-lock.
+ if (eventMode == EventMode::ALWAYS || valueUpdated) {
+ onValueChangeCallback(updatedValue);
}
return {};
}
diff --git a/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
index fea5034..625652e 100644
--- a/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
@@ -509,6 +509,24 @@
ASSERT_EQ(updatedValue.prop, INVALID_PROP_ID);
}
+TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackUseVehiclePropertyStore_noDeadLock) {
+ VehiclePropValue fuelCapacity = {
+ .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+ .value = {.floatValues = {1.0}},
+ };
+
+ std::vector<VehiclePropConfig> configs;
+
+ mStore->setOnValueChangeCallback(
+ [this, &configs]([[maybe_unused]] const VehiclePropValue& value) {
+ configs = mStore->getAllConfigs();
+ });
+
+ ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity), /*updateStatus=*/true,
+ VehiclePropertyStore::EventMode::ALWAYS));
+ ASSERT_EQ(configs.size(), static_cast<size_t>(2));
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
index b91895e..5053c96 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
@@ -36,20 +36,29 @@
namespace automotive {
namespace vehicle {
+// A structure to represent subscription config for one subscription client.
+struct SubConfig {
+ float sampleRateHz;
+ bool enableVur;
+};
+
// A class to represent all the subscription configs for a continuous [propId, areaId].
class ContSubConfigs final {
public:
using ClientIdType = const AIBinder*;
- void addClient(const ClientIdType& clientId, float sampleRateHz);
+ void addClient(const ClientIdType& clientId, float sampleRateHz, bool enableVur);
void removeClient(const ClientIdType& clientId);
float getMaxSampleRateHz() const;
+ bool isVurEnabled() const;
+ bool isVurEnabledForClient(const ClientIdType& clientId);
private:
float mMaxSampleRateHz = 0.;
- std::unordered_map<ClientIdType, float> mSampleRateHzByClient;
+ bool mEnableVur;
+ std::unordered_map<ClientIdType, SubConfig> mConfigByClient;
- void refreshMaxSampleRateHz();
+ void refreshCombinedConfig();
};
// A thread-safe subscription manager that manages all VHAL subscriptions.
@@ -58,6 +67,7 @@
using ClientIdType = const AIBinder*;
using CallbackType =
std::shared_ptr<aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
+ using VehiclePropValue = aidl::android::hardware::automotive::vehicle::VehiclePropValue;
explicit SubscriptionManager(IVehicleHardware* vehicleHardware);
~SubscriptionManager();
@@ -92,11 +102,8 @@
// For a list of updated properties, returns a map that maps clients subscribing to
// the updated properties to a list of updated values. This would only return on-change property
// clients that should be informed for the given updated values.
- std::unordered_map<CallbackType,
- std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>>
- getSubscribedClients(
- std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&&
- updatedValues);
+ std::unordered_map<CallbackType, std::vector<VehiclePropValue>> getSubscribedClients(
+ std::vector<VehiclePropValue>&& updatedValues);
// For a list of set property error events, returns a map that maps clients subscribing to the
// properties to a list of errors for each client.
@@ -116,28 +123,60 @@
IVehicleHardware* mVehicleHardware;
+ struct VehiclePropValueHashPropIdAreaId {
+ inline size_t operator()(const VehiclePropValue& vehiclePropValue) const {
+ size_t res = 0;
+ hashCombine(res, vehiclePropValue.prop);
+ hashCombine(res, vehiclePropValue.areaId);
+ return res;
+ }
+ };
+
+ struct VehiclePropValueEqualPropIdAreaId {
+ inline bool operator()(const VehiclePropValue& left, const VehiclePropValue& right) const {
+ return left.prop == right.prop && left.areaId == right.areaId;
+ }
+ };
+
mutable std::mutex mLock;
std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
PropIdAreaIdHash>
- mClientsByPropIdArea GUARDED_BY(mLock);
+ mClientsByPropIdAreaId GUARDED_BY(mLock);
std::unordered_map<ClientIdType, std::unordered_set<PropIdAreaId, PropIdAreaIdHash>>
mSubscribedPropsByClient GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, ContSubConfigs, PropIdAreaIdHash> mContSubConfigsByPropIdArea
GUARDED_BY(mLock);
+ std::unordered_map<CallbackType,
+ std::unordered_set<VehiclePropValue, VehiclePropValueHashPropIdAreaId,
+ VehiclePropValueEqualPropIdAreaId>>
+ mContSubValuesByCallback GUARDED_BY(mLock);
VhalResult<void> addContinuousSubscriberLocked(const ClientIdType& clientId,
const PropIdAreaId& propIdAreaId,
- float sampleRateHz) REQUIRES(mLock);
+ float sampleRateHz, bool enableVur)
+ REQUIRES(mLock);
+ VhalResult<void> addOnChangeSubscriberLocked(const PropIdAreaId& propIdAreaId) REQUIRES(mLock);
+ // Removes the subscription client for the continuous [propId, areaId].
VhalResult<void> removeContinuousSubscriberLocked(const ClientIdType& clientId,
const PropIdAreaId& propIdAreaId)
REQUIRES(mLock);
+ // Removes one subscription client for the on-change [propId, areaId].
+ VhalResult<void> removeOnChangeSubscriberLocked(const PropIdAreaId& propIdAreaId)
+ REQUIRES(mLock);
- VhalResult<void> updateContSubConfigs(const PropIdAreaId& PropIdAreaId,
- const ContSubConfigs& newConfig) REQUIRES(mLock);
+ VhalResult<void> updateContSubConfigsLocked(const PropIdAreaId& PropIdAreaId,
+ const ContSubConfigs& newConfig) REQUIRES(mLock);
+
+ VhalResult<void> unsubscribePropIdAreaIdLocked(SubscriptionManager::ClientIdType clientId,
+ const PropIdAreaId& propIdAreaId)
+ REQUIRES(mLock);
// Checks whether the manager is empty. For testing purpose.
bool isEmpty();
+ bool isValueUpdatedLocked(const CallbackType& callback, const VehiclePropValue& value)
+ REQUIRES(mLock);
+
// Get the interval in nanoseconds accroding to sample rate.
static android::base::Result<int64_t> getIntervalNanos(float sampleRateHz);
};
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index 111a6ec..d85cc09 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -695,7 +695,39 @@
if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
optionCopy.sampleRate = getDefaultSampleRateHz(
optionCopy.sampleRate, config.minSampleRate, config.maxSampleRate);
- continuousSubscriptions.push_back(std::move(optionCopy));
+ if (!optionCopy.enableVariableUpdateRate) {
+ continuousSubscriptions.push_back(std::move(optionCopy));
+ } else {
+ // If clients enables to VUR, we need to check whether VUR is supported for the
+ // specific [propId, areaId] and overwrite the option to disable if not supported.
+ std::vector<int32_t> areasVurEnabled;
+ std::vector<int32_t> areasVurDisabled;
+ for (int32_t areaId : optionCopy.areaIds) {
+ const VehicleAreaConfig* areaConfig = getAreaConfig(propId, areaId, config);
+ if (areaConfig == nullptr) {
+ areasVurDisabled.push_back(areaId);
+ continue;
+ }
+ if (!areaConfig->supportVariableUpdateRate) {
+ areasVurDisabled.push_back(areaId);
+ continue;
+ }
+ areasVurEnabled.push_back(areaId);
+ }
+ if (!areasVurEnabled.empty()) {
+ SubscribeOptions optionVurEnabled = optionCopy;
+ optionVurEnabled.areaIds = areasVurEnabled;
+ optionVurEnabled.enableVariableUpdateRate = true;
+ continuousSubscriptions.push_back(std::move(optionVurEnabled));
+ }
+
+ if (!areasVurDisabled.empty()) {
+ // We use optionCopy for areas with VUR disabled.
+ optionCopy.areaIds = areasVurDisabled;
+ optionCopy.enableVariableUpdateRate = false;
+ continuousSubscriptions.push_back(std::move(optionCopy));
+ }
+ }
} else {
onChangeSubscriptions.push_back(std::move(optionCopy));
}
diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
index b3c2693..29d81a7 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
@@ -16,6 +16,7 @@
#include "SubscriptionManager.h"
+#include <VehicleUtils.h>
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -29,10 +30,6 @@
namespace {
-constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.;
-
-} // namespace
-
using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
@@ -43,13 +40,28 @@
using ::android::base::StringPrintf;
using ::ndk::ScopedAStatus;
+constexpr float ONE_SECOND_IN_NANOS = 1'000'000'000.;
+
+SubscribeOptions newSubscribeOptions(int32_t propId, int32_t areaId, float sampleRateHz,
+ bool enableVur) {
+ SubscribeOptions subscribedOptions;
+ subscribedOptions.propId = propId;
+ subscribedOptions.areaIds = {areaId};
+ subscribedOptions.sampleRate = sampleRateHz;
+ subscribedOptions.enableVariableUpdateRate = enableVur;
+
+ return subscribedOptions;
+}
+
+} // namespace
+
SubscriptionManager::SubscriptionManager(IVehicleHardware* vehicleHardware)
: mVehicleHardware(vehicleHardware) {}
SubscriptionManager::~SubscriptionManager() {
std::scoped_lock<std::mutex> lockGuard(mLock);
- mClientsByPropIdArea.clear();
+ mClientsByPropIdAreaId.clear();
mSubscribedPropsByClient.clear();
}
@@ -62,45 +74,83 @@
if (sampleRateHz <= 0) {
return Error() << "invalid sample rate, must be a positive number";
}
- if (sampleRateHz <= (ONE_SECOND_IN_NANO / static_cast<float>(INT64_MAX))) {
+ if (sampleRateHz <= (ONE_SECOND_IN_NANOS / static_cast<float>(INT64_MAX))) {
return Error() << "invalid sample rate: " << sampleRateHz << ", too small";
}
- intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANO / sampleRateHz);
+ intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANOS / sampleRateHz);
return intervalNanos;
}
-void ContSubConfigs::refreshMaxSampleRateHz() {
+void ContSubConfigs::refreshCombinedConfig() {
float maxSampleRateHz = 0.;
+ bool enableVur = true;
// This is not called frequently so a brute-focre is okay. More efficient way exists but this
// is simpler.
- for (const auto& [_, sampleRateHz] : mSampleRateHzByClient) {
- if (sampleRateHz > maxSampleRateHz) {
- maxSampleRateHz = sampleRateHz;
+ for (const auto& [_, subConfig] : mConfigByClient) {
+ if (subConfig.sampleRateHz > maxSampleRateHz) {
+ maxSampleRateHz = subConfig.sampleRateHz;
+ }
+ if (!subConfig.enableVur) {
+ // If one client does not enable variable update rate, we cannot enable variable update
+ // rate in IVehicleHardware.
+ enableVur = false;
}
}
mMaxSampleRateHz = maxSampleRateHz;
+ mEnableVur = enableVur;
}
-void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRateHz) {
- mSampleRateHzByClient[clientId] = sampleRateHz;
- refreshMaxSampleRateHz();
+void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRateHz, bool enableVur) {
+ mConfigByClient[clientId] = {
+ .sampleRateHz = sampleRateHz,
+ .enableVur = enableVur,
+ };
+ refreshCombinedConfig();
}
void ContSubConfigs::removeClient(const ClientIdType& clientId) {
- mSampleRateHzByClient.erase(clientId);
- refreshMaxSampleRateHz();
+ mConfigByClient.erase(clientId);
+ refreshCombinedConfig();
}
float ContSubConfigs::getMaxSampleRateHz() const {
return mMaxSampleRateHz;
}
+bool ContSubConfigs::isVurEnabled() const {
+ return mEnableVur;
+}
+
+bool ContSubConfigs::isVurEnabledForClient(const ClientIdType& clientId) {
+ return mConfigByClient[clientId].enableVur;
+}
+
+VhalResult<void> SubscriptionManager::addOnChangeSubscriberLocked(
+ const PropIdAreaId& propIdAreaId) {
+ if (mClientsByPropIdAreaId.find(propIdAreaId) != mClientsByPropIdAreaId.end()) {
+ // This propId, areaId is already subscribed, ignore the request.
+ return {};
+ }
+
+ int32_t propId = propIdAreaId.propId;
+ int32_t areaId = propIdAreaId.areaId;
+ if (auto status = mVehicleHardware->subscribe(
+ newSubscribeOptions(propId, areaId, /*updateRateHz=*/0, /*enableVur*/ false));
+ status != StatusCode::OK) {
+ return StatusError(status)
+ << StringPrintf("failed subscribe for prop: %s, areaId: %" PRId32,
+ propIdToString(propId).c_str(), areaId);
+ }
+ return {};
+}
+
VhalResult<void> SubscriptionManager::addContinuousSubscriberLocked(
- const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz) {
+ const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz,
+ bool enableVur) {
// Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
- newConfig.addClient(clientId, sampleRateHz);
- return updateContSubConfigs(propIdAreaId, newConfig);
+ newConfig.addClient(clientId, sampleRateHz, enableVur);
+ return updateContSubConfigsLocked(propIdAreaId, newConfig);
}
VhalResult<void> SubscriptionManager::removeContinuousSubscriberLocked(
@@ -108,25 +158,62 @@
// Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
newConfig.removeClient(clientId);
- return updateContSubConfigs(propIdAreaId, newConfig);
+ return updateContSubConfigsLocked(propIdAreaId, newConfig);
}
-VhalResult<void> SubscriptionManager::updateContSubConfigs(const PropIdAreaId& propIdAreaId,
- const ContSubConfigs& newConfig) {
- if (newConfig.getMaxSampleRateHz() ==
- mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRateHz()) {
+VhalResult<void> SubscriptionManager::removeOnChangeSubscriberLocked(
+ const PropIdAreaId& propIdAreaId) {
+ if (mClientsByPropIdAreaId[propIdAreaId].size() > 1) {
+ // After unsubscribing this client, there is still client subscribed, so do nothing.
+ return {};
+ }
+
+ int32_t propId = propIdAreaId.propId;
+ int32_t areaId = propIdAreaId.areaId;
+ if (auto status = mVehicleHardware->unsubscribe(propId, areaId); status != StatusCode::OK) {
+ return StatusError(status)
+ << StringPrintf("failed unsubscribe for prop: %s, areaId: %" PRId32,
+ propIdToString(propId).c_str(), areaId);
+ }
+ return {};
+}
+
+VhalResult<void> SubscriptionManager::updateContSubConfigsLocked(const PropIdAreaId& propIdAreaId,
+ const ContSubConfigs& newConfig) {
+ const auto& oldConfig = mContSubConfigsByPropIdArea[propIdAreaId];
+ float newRateHz = newConfig.getMaxSampleRateHz();
+ float oldRateHz = oldConfig.getMaxSampleRateHz();
+ if (newRateHz == oldRateHz && newConfig.isVurEnabled() == oldConfig.isVurEnabled()) {
mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
return {};
}
- float newRateHz = newConfig.getMaxSampleRateHz();
int32_t propId = propIdAreaId.propId;
int32_t areaId = propIdAreaId.areaId;
- if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRateHz);
- status != StatusCode::OK) {
- return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32
- ", area"
- ": %" PRId32 ", sample rate: %f HZ",
- propId, areaId, newRateHz);
+ if (newRateHz != oldRateHz) {
+ if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRateHz);
+ status != StatusCode::OK) {
+ return StatusError(status)
+ << StringPrintf("failed to update sample rate for prop: %s, areaId: %" PRId32
+ ", sample rate: %f HZ",
+ propIdToString(propId).c_str(), areaId, newRateHz);
+ }
+ }
+ if (newRateHz != 0) {
+ if (auto status = mVehicleHardware->subscribe(
+ newSubscribeOptions(propId, areaId, newRateHz, newConfig.isVurEnabled()));
+ status != StatusCode::OK) {
+ return StatusError(status) << StringPrintf(
+ "failed subscribe for prop: %s, areaId"
+ ": %" PRId32 ", sample rate: %f HZ",
+ propIdToString(propId).c_str(), areaId, newRateHz);
+ }
+ } else {
+ if (auto status = mVehicleHardware->unsubscribe(propId, areaId); status != StatusCode::OK) {
+ return StatusError(status) << StringPrintf(
+ "failed unsubscribe for prop: %s, areaId"
+ ": %" PRId32,
+ propIdToString(propId).c_str(), areaId);
+ }
}
mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
return {};
@@ -163,21 +250,54 @@
.propId = propId,
.areaId = areaId,
};
+ VhalResult<void> result;
if (isContinuousProperty) {
- if (auto result = addContinuousSubscriberLocked(clientId, propIdAreaId,
- option.sampleRate);
- !result.ok()) {
- return result;
- }
+ result = addContinuousSubscriberLocked(clientId, propIdAreaId, option.sampleRate,
+ option.enableVariableUpdateRate);
+ } else {
+ result = addOnChangeSubscriberLocked(propIdAreaId);
+ }
+
+ if (!result.ok()) {
+ return result;
}
mSubscribedPropsByClient[clientId].insert(propIdAreaId);
- mClientsByPropIdArea[propIdAreaId][clientId] = callback;
+ mClientsByPropIdAreaId[propIdAreaId][clientId] = callback;
}
}
return {};
}
+VhalResult<void> SubscriptionManager::unsubscribePropIdAreaIdLocked(
+ SubscriptionManager::ClientIdType clientId, const PropIdAreaId& propIdAreaId) {
+ if (mContSubConfigsByPropIdArea.find(propIdAreaId) != mContSubConfigsByPropIdArea.end()) {
+ // This is a subscribed continuous property.
+ if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
+ return result;
+ }
+ } else {
+ if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
+ ALOGW("Unsubscribe: The property: %s, areaId: %" PRId32
+ " was not previously subscribed, do nothing",
+ propIdToString(propIdAreaId.propId).c_str(), propIdAreaId.areaId);
+ return {};
+ }
+ // This is an on-change property.
+ if (auto result = removeOnChangeSubscriberLocked(propIdAreaId); !result.ok()) {
+ return result;
+ }
+ }
+
+ auto& clients = mClientsByPropIdAreaId[propIdAreaId];
+ clients.erase(clientId);
+ if (clients.empty()) {
+ mClientsByPropIdAreaId.erase(propIdAreaId);
+ mContSubConfigsByPropIdArea.erase(propIdAreaId);
+ }
+ return {};
+}
+
VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId,
const std::vector<int32_t>& propIds) {
std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -186,39 +306,27 @@
return StatusError(StatusCode::INVALID_ARG)
<< "No property was subscribed for the callback";
}
- std::unordered_set<int32_t> subscribedPropIds;
- for (auto const& propIdAreaId : mSubscribedPropsByClient[clientId]) {
- subscribedPropIds.insert(propIdAreaId.propId);
- }
+ std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
+ std::unordered_set<int32_t> propIdSet;
for (int32_t propId : propIds) {
- if (subscribedPropIds.find(propId) == subscribedPropIds.end()) {
- return StatusError(StatusCode::INVALID_ARG)
- << "property ID: " << propId << " is not subscribed";
+ propIdSet.insert(propId);
+ }
+ auto& subscribedPropIdsAreaIds = mSubscribedPropsByClient[clientId];
+ for (const auto& propIdAreaId : subscribedPropIdsAreaIds) {
+ if (propIdSet.find(propIdAreaId.propId) != propIdSet.end()) {
+ propIdAreaIdsToUnsubscribe.push_back(propIdAreaId);
}
}
- auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
- auto it = propIdAreaIds.begin();
- while (it != propIdAreaIds.end()) {
- int32_t propId = it->propId;
- if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) {
- if (auto result = removeContinuousSubscriberLocked(clientId, *it); !result.ok()) {
- return result;
- }
-
- auto& clients = mClientsByPropIdArea[*it];
- clients.erase(clientId);
- if (clients.empty()) {
- mClientsByPropIdArea.erase(*it);
- mContSubConfigsByPropIdArea.erase(*it);
- }
- it = propIdAreaIds.erase(it);
- } else {
- it++;
+ for (const auto& propIdAreaId : propIdAreaIdsToUnsubscribe) {
+ if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
+ return result;
}
+ subscribedPropIdsAreaIds.erase(propIdAreaId);
}
- if (propIdAreaIds.empty()) {
+
+ if (subscribedPropIdsAreaIds.empty()) {
mSubscribedPropsByClient.erase(clientId);
}
return {};
@@ -233,21 +341,42 @@
auto& subscriptions = mSubscribedPropsByClient[clientId];
for (auto const& propIdAreaId : subscriptions) {
- if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
+ if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
return result;
}
-
- auto& clients = mClientsByPropIdArea[propIdAreaId];
- clients.erase(clientId);
- if (clients.empty()) {
- mClientsByPropIdArea.erase(propIdAreaId);
- mContSubConfigsByPropIdArea.erase(propIdAreaId);
- }
}
mSubscribedPropsByClient.erase(clientId);
return {};
}
+bool SubscriptionManager::isValueUpdatedLocked(const std::shared_ptr<IVehicleCallback>& callback,
+ const VehiclePropValue& value) {
+ const auto& it = mContSubValuesByCallback[callback].find(value);
+ if (it == mContSubValuesByCallback[callback].end()) {
+ mContSubValuesByCallback[callback].insert(value);
+ return true;
+ }
+
+ if (it->timestamp > value.timestamp) {
+ ALOGE("The updated property value: %s is outdated, ignored", value.toString().c_str());
+ return false;
+ }
+
+ if (it->value == value.value && it->status == value.status) {
+ // Even though the property value is the same, we need to store the new property event to
+ // update the timestamp.
+ mContSubValuesByCallback[callback].insert(value);
+ ALOGD("The updated property value for propId: %" PRId32 ", areaId: %" PRId32
+ " has the "
+ "same value and status, ignored if VUR is enabled",
+ it->prop, it->areaId);
+ return false;
+ }
+
+ mContSubValuesByCallback[callback].insert(value);
+ return true;
+}
+
std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropValue>>
SubscriptionManager::getSubscribedClients(std::vector<VehiclePropValue>&& updatedValues) {
std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -258,12 +387,22 @@
.propId = value.prop,
.areaId = value.areaId,
};
- if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
+ if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
continue;
}
- for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
- clients[client].push_back(value);
+ for (const auto& [client, callback] : mClientsByPropIdAreaId[propIdAreaId]) {
+ auto& subConfigs = mContSubConfigsByPropIdArea[propIdAreaId];
+ // If client wants VUR (and VUR is supported as checked in DefaultVehicleHal), it is
+ // possible that VUR is not enabled in IVehicleHardware because another client does not
+ // enable VUR. We will implement VUR filtering here for the client that enables it.
+ if (subConfigs.isVurEnabledForClient(client) && !subConfigs.isVurEnabled()) {
+ if (isValueUpdatedLocked(callback, value)) {
+ clients[callback].push_back(value);
+ }
+ } else {
+ clients[callback].push_back(value);
+ }
}
}
return clients;
@@ -280,11 +419,11 @@
.propId = errorEvent.propId,
.areaId = errorEvent.areaId,
};
- if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
+ if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
continue;
}
- for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
+ for (const auto& [_, client] : mClientsByPropIdAreaId[propIdAreaId]) {
clients[client].push_back({
.propId = errorEvent.propId,
.areaId = errorEvent.areaId,
@@ -297,7 +436,7 @@
bool SubscriptionManager::isEmpty() {
std::scoped_lock<std::mutex> lockGuard(mLock);
- return mSubscribedPropsByClient.empty() && mClientsByPropIdArea.empty();
+ return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty();
}
size_t SubscriptionManager::countClients() {
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index e775612..7195d97 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -98,12 +98,22 @@
constexpr int32_t READ_ONLY_PROP = 10006 + 0x10000000 + 0x01000000 + 0x00400000;
// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
constexpr int32_t WRITE_ONLY_PROP = 10007 + 0x10000000 + 0x01000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+constexpr int32_t GLOBAL_CONTINUOUS_PROP_NO_VUR = 10008 + 0x10000000 + 0x01000000 + 0x00400000;
int32_t testInt32VecProp(size_t i) {
// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
return static_cast<int32_t>(i) + 0x10000000 + 0x01000000 + 0x00410000;
}
+std::string toString(const std::vector<SubscribeOptions>& options) {
+ std::string optionsStr;
+ for (const auto& option : options) {
+ optionsStr += option.toString() + "\n";
+ }
+ return optionsStr;
+}
+
struct PropConfigCmp {
bool operator()(const VehiclePropConfig& a, const VehiclePropConfig& b) const {
return (a.prop < b.prop);
@@ -245,8 +255,18 @@
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
});
// A global continuous property.
+ testConfigs.push_back(VehiclePropConfig{.prop = GLOBAL_CONTINUOUS_PROP,
+ .access = VehiclePropertyAccess::READ_WRITE,
+ .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+ .minSampleRate = 0.0,
+ .maxSampleRate = 100.0,
+ .areaConfigs = {{
+ .areaId = 0,
+ .supportVariableUpdateRate = true,
+ }}});
+ // A global continuous property that does not support VUR.
testConfigs.push_back(VehiclePropConfig{
- .prop = GLOBAL_CONTINUOUS_PROP,
+ .prop = GLOBAL_CONTINUOUS_PROP_NO_VUR,
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::CONTINUOUS,
.minSampleRate = 0.0,
@@ -286,11 +306,13 @@
.areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
.minInt32Value = 0,
.maxInt32Value = 100,
+ .supportVariableUpdateRate = true,
},
{
.areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
.minInt32Value = 0,
.maxInt32Value = 100,
+ .supportVariableUpdateRate = false,
},
},
});
@@ -1352,6 +1374,62 @@
EXPECT_EQ(countClients(), static_cast<size_t>(1));
}
+TEST_F(DefaultVehicleHalTest, testSubscribeContinuous_propNotSupportVur) {
+ std::vector<SubscribeOptions> options = {
+ {
+ .propId = GLOBAL_CONTINUOUS_PROP,
+ .sampleRate = 20.0,
+ .enableVariableUpdateRate = true,
+ },
+ {
+ .propId = GLOBAL_CONTINUOUS_PROP_NO_VUR,
+ .sampleRate = 30.0,
+ .enableVariableUpdateRate = true,
+ },
+ };
+
+ auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+ ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+ auto receivedSubscribeOptions = getHardware()->getSubscribeOptions();
+ ASSERT_THAT(receivedSubscribeOptions, UnorderedElementsAre(
+ SubscribeOptions{
+ .propId = GLOBAL_CONTINUOUS_PROP,
+ .areaIds = {0},
+ .enableVariableUpdateRate = true,
+ .sampleRate = 20.0,
+ },
+ SubscribeOptions{
+ .propId = GLOBAL_CONTINUOUS_PROP_NO_VUR,
+ .areaIds = {0},
+ .enableVariableUpdateRate = false,
+ .sampleRate = 30.0,
+ }))
+ << "received unexpected subscribe options: " << toString(receivedSubscribeOptions);
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeContinuous_propSupportVurNotEnabled) {
+ std::vector<SubscribeOptions> options = {
+ {
+ .propId = GLOBAL_CONTINUOUS_PROP,
+ .sampleRate = 20.0,
+ .enableVariableUpdateRate = false,
+ },
+ };
+
+ auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+ ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+ auto receivedSubscribeOptions = getHardware()->getSubscribeOptions();
+ ASSERT_THAT(receivedSubscribeOptions, UnorderedElementsAre(SubscribeOptions{
+ .propId = GLOBAL_CONTINUOUS_PROP,
+ .areaIds = {0},
+ .enableVariableUpdateRate = false,
+ .sampleRate = 20.0,
+ }))
+ << "received unexpected subscribe options: " << toString(receivedSubscribeOptions);
+}
+
TEST_F(DefaultVehicleHalTest, testSubscribeAreaContinuous) {
std::vector<SubscribeOptions> options = {
{
@@ -1404,6 +1482,44 @@
ASSERT_GE(rightCount, static_cast<size_t>(5));
}
+TEST_F(DefaultVehicleHalTest, testAreaContinuous_areaNotSupportVur) {
+ std::vector<SubscribeOptions> options = {
+ {
+ .propId = AREA_CONTINUOUS_PROP,
+ .sampleRate = 20.0,
+ .areaIds = {toInt(VehicleAreaWindow::ROW_1_LEFT)},
+ .enableVariableUpdateRate = true,
+ },
+ {
+ .propId = AREA_CONTINUOUS_PROP,
+ .sampleRate = 10.0,
+ .areaIds = {toInt(VehicleAreaWindow::ROW_1_RIGHT)},
+ .enableVariableUpdateRate = true,
+ },
+ };
+
+ auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+ ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+ auto receivedSubscribeOptions = getHardware()->getSubscribeOptions();
+ ASSERT_THAT(receivedSubscribeOptions,
+ UnorderedElementsAre(
+ SubscribeOptions{
+ .propId = AREA_CONTINUOUS_PROP,
+ .sampleRate = 20.0,
+ .areaIds = {toInt(VehicleAreaWindow::ROW_1_LEFT)},
+ .enableVariableUpdateRate = true,
+ },
+ SubscribeOptions{
+ .propId = AREA_CONTINUOUS_PROP,
+ .sampleRate = 10.0,
+ .areaIds = {toInt(VehicleAreaWindow::ROW_1_RIGHT)},
+ // Area2 actually does not support VUR.
+ .enableVariableUpdateRate = false,
+ }))
+ << "received unexpected subscribe options: " << toString(receivedSubscribeOptions);
+}
+
TEST_F(DefaultVehicleHalTest, testUnsubscribeOnChange) {
std::vector<SubscribeOptions> options = {
{
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
index 3fae596..db15c89 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
@@ -29,6 +29,7 @@
using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
@@ -88,7 +89,40 @@
return StatusCode::OK;
}
-StatusCode MockVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) {
+StatusCode MockVehicleHardware::subscribe(SubscribeOptions options) {
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mSubscribeOptions.push_back(options);
+ }
+ for (int32_t areaId : options.areaIds) {
+ if (auto status = subscribePropIdAreaId(options.propId, areaId, options.sampleRate);
+ status != StatusCode::OK) {
+ return status;
+ }
+ }
+ return StatusCode::OK;
+}
+
+std::vector<SubscribeOptions> MockVehicleHardware::getSubscribeOptions() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ return mSubscribeOptions;
+}
+
+void MockVehicleHardware::clearSubscribeOptions() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mSubscribeOptions.clear();
+}
+
+StatusCode MockVehicleHardware::subscribePropIdAreaId(int32_t propId, int32_t areaId,
+ float sampleRateHz) {
+ if (sampleRateHz == 0) {
+ // on-change property.
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mSubOnChangePropIdAreaIds.insert(std::pair<int32_t, int32_t>(propId, areaId));
+ return StatusCode::OK;
+ }
+
+ // continuous property.
std::shared_ptr<std::function<void()>> action;
{
@@ -97,9 +131,6 @@
// Remove the previous action register for this [propId, areaId].
mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propId][areaId]);
}
- if (sampleRate == 0) {
- return StatusCode::OK;
- }
// We are sure 'propertyChangeCallback' would be alive because we would unregister timer
// before destroying 'this' which owns mPropertyChangeCallback.
@@ -107,8 +138,8 @@
action = std::make_shared<std::function<void()>>([propertyChangeCallback, propId, areaId] {
std::vector<VehiclePropValue> values = {
{
- .prop = propId,
.areaId = areaId,
+ .prop = propId,
},
};
(*propertyChangeCallback)(values);
@@ -119,11 +150,45 @@
// In mock implementation, we generate a new property change event for this property at sample
// rate.
- int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate);
+ int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRateHz);
mRecurrentTimer->registerTimerCallback(interval, action);
return StatusCode::OK;
}
+StatusCode MockVehicleHardware::unsubscribe(int32_t propId, int32_t areaId) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ // For on-change property.
+ mSubOnChangePropIdAreaIds.erase(std::make_pair(propId, areaId));
+ // for continuous property.
+ if (mRecurrentActions[propId][areaId] != nullptr) {
+ // Remove the previous action register for this [propId, areaId].
+ mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propId][areaId]);
+ mRecurrentActions[propId].erase(areaId);
+ if (mRecurrentActions[propId].empty()) {
+ mRecurrentActions.erase(propId);
+ }
+ }
+ return StatusCode::OK;
+}
+
+std::set<std::pair<int32_t, int32_t>> MockVehicleHardware::getSubscribedOnChangePropIdAreaIds() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ std::set<std::pair<int32_t, int32_t>> propIdAreaIds;
+ propIdAreaIds = mSubOnChangePropIdAreaIds;
+ return propIdAreaIds;
+}
+
+std::set<std::pair<int32_t, int32_t>> MockVehicleHardware::getSubscribedContinuousPropIdAreaIds() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ std::set<std::pair<int32_t, int32_t>> propIdAreaIds;
+ for (const auto& [propId, actionByAreaId] : mRecurrentActions) {
+ for (const auto& [areaId, _] : actionByAreaId) {
+ propIdAreaIds.insert(std::make_pair(propId, areaId));
+ }
+ }
+ return propIdAreaIds;
+}
+
void MockVehicleHardware::registerOnPropertyChangeEvent(
std::unique_ptr<const PropertyChangeCallback> callback) {
std::scoped_lock<std::mutex> lockGuard(mLock);
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
index 3ce18c5..eeca582 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
@@ -29,6 +29,7 @@
#include <list>
#include <memory>
#include <mutex>
+#include <set>
#include <thread>
#include <unordered_map>
#include <vector>
@@ -59,8 +60,10 @@
void registerOnPropertyChangeEvent(
std::unique_ptr<const PropertyChangeCallback> callback) override;
void registerOnPropertySetErrorEvent(std::unique_ptr<const PropertySetErrorCallback>) override;
- aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
- int32_t propId, int32_t areaId, float sampleRate) override;
+ aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
+ aidl::android::hardware::automotive::vehicle::SubscribeOptions options) override;
+ aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(int32_t propId,
+ int32_t areaId) override;
std::chrono::nanoseconds getPropertyOnChangeEventBatchingWindow() override;
// Test functions.
@@ -90,6 +93,12 @@
void sendOnPropertySetErrorEvent(const std::vector<SetValueErrorEvent>& errorEvents);
void setPropertyOnChangeEventBatchingWindow(std::chrono::nanoseconds window);
+ std::set<std::pair<int32_t, int32_t>> getSubscribedOnChangePropIdAreaIds();
+ std::set<std::pair<int32_t, int32_t>> getSubscribedContinuousPropIdAreaIds();
+ std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>
+ getSubscribeOptions();
+ void clearSubscribeOptions();
+
private:
mutable std::mutex mLock;
mutable std::condition_variable mCv;
@@ -114,6 +123,9 @@
const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>&)>
mGetValueResponder GUARDED_BY(mLock);
std::chrono::nanoseconds mEventBatchingWindow GUARDED_BY(mLock) = std::chrono::nanoseconds(0);
+ std::set<std::pair<int32_t, int32_t>> mSubOnChangePropIdAreaIds GUARDED_BY(mLock);
+ std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions> mSubscribeOptions
+ GUARDED_BY(mLock);
template <class ResultType>
aidl::android::hardware::automotive::vehicle::StatusCode returnResponse(
@@ -126,6 +138,8 @@
const std::vector<RequestType>& requests,
std::list<std::vector<RequestType>>* storedRequests,
std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock);
+ aidl::android::hardware::automotive::vehicle::StatusCode subscribePropIdAreaId(
+ int32_t propId, int32_t areaId, float sampleRateHz);
DumpResult mDumpResult;
diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
index 5464304..aa5f003 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
@@ -43,10 +43,12 @@
using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
using ::ndk::ScopedAStatus;
using ::ndk::SpAIBinder;
+using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::UnorderedElementsAre;
@@ -114,6 +116,8 @@
void clearEvents() { return getCallback()->clearEvents(); }
+ std::shared_ptr<MockVehicleHardware> getHardware() { return mHardware; }
+
private:
std::unique_ptr<SubscriptionManager> mManager;
std::shared_ptr<PropertyCallback> mCallback;
@@ -132,6 +136,9 @@
auto result = getManager()->subscribe(getCallbackClient(), options, true);
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+ ASSERT_THAT(getHardware()->getSubscribedContinuousPropIdAreaIds(),
+ UnorderedElementsAre(std::pair<int32_t, int32_t>(0, 0)));
+
std::this_thread::sleep_for(std::chrono::seconds(1));
// Theoretically trigger 10 times, but check for at least 9 times to be stable.
@@ -240,6 +247,8 @@
result = getManager()->unsubscribe(getCallbackClient()->asBinder().get());
ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
+ ASSERT_EQ(getHardware()->getSubscribedContinuousPropIdAreaIds().size(), 0u);
+
// Wait for the last events to come.
std::this_thread::sleep_for(std::chrono::milliseconds(100));
@@ -316,7 +325,7 @@
EXPECT_TRUE(getEvents().empty());
}
-TEST_F(SubscriptionManagerTest, testUnsubscribeFailure) {
+TEST_F(SubscriptionManagerTest, testUnsubscribeUnsubscribedPropId) {
std::vector<SubscribeOptions> options = {
{
.propId = 0,
@@ -334,14 +343,21 @@
// Property ID: 2 was not subscribed.
result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
std::vector<int32_t>({0, 1, 2}));
- ASSERT_FALSE(result.ok()) << "unsubscribe an unsubscribed property must fail";
+ ASSERT_TRUE(result.ok()) << "unsubscribe an unsubscribed property must do nothing";
- // Since property 0 and property 1 was not unsubscribed successfully, we should be able to
- // unsubscribe them again.
- result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
- std::vector<int32_t>({0, 1}));
- ASSERT_TRUE(result.ok()) << "a failed unsubscription must not unsubscribe any properties"
- << result.error().message();
+ std::vector<VehiclePropValue> updatedValues = {
+ {
+ .prop = 0,
+ .areaId = 0,
+ },
+ {
+ .prop = 1,
+ .areaId = 0,
+ },
+ };
+ auto clients = getManager()->getSubscribedClients(std::vector<VehiclePropValue>(updatedValues));
+
+ ASSERT_EQ(clients.size(), 0u) << "all subscribed properties must be unsubscribed";
}
TEST_F(SubscriptionManagerTest, testSubscribeOnchange) {
@@ -370,6 +386,11 @@
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
result = getManager()->subscribe(client2, options2, false);
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+ ASSERT_THAT(getHardware()->getSubscribedOnChangePropIdAreaIds(),
+ UnorderedElementsAre(std::pair<int32_t, int32_t>(0, 0),
+ std::pair<int32_t, int32_t>(0, 1),
+ std::pair<int32_t, int32_t>(1, 0)));
+ ASSERT_EQ(getHardware()->getSubscribedContinuousPropIdAreaIds().size(), 0u);
std::vector<VehiclePropValue> updatedValues = {
{
@@ -483,6 +504,8 @@
auto clients = getManager()->getSubscribedClients(std::vector<VehiclePropValue>(updatedValues));
ASSERT_THAT(clients[getCallbackClient()], ElementsAre(updatedValues[1]));
+ ASSERT_THAT(getHardware()->getSubscribedOnChangePropIdAreaIds(),
+ UnorderedElementsAre(std::pair<int32_t, int32_t>(1, 0)));
}
TEST_F(SubscriptionManagerTest, testCheckSampleRateHzValid) {
@@ -497,6 +520,257 @@
ASSERT_FALSE(SubscriptionManager::checkSampleRateHz(0));
}
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur) {
+ std::vector<SubscribeOptions> options = {{
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 10.0,
+ .enableVariableUpdateRate = true,
+ }};
+
+ auto result = getManager()->subscribe(getCallbackClient(), options, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ ASSERT_THAT(getHardware()->getSubscribeOptions(), ElementsAre(options[0]));
+}
+
+TEST_F(SubscriptionManagerTest, testSubscribe_VurStateChange) {
+ std::vector<SubscribeOptions> options = {{
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 10.0,
+ .enableVariableUpdateRate = true,
+ }};
+
+ auto result = getManager()->subscribe(getCallbackClient(), options, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ ASSERT_THAT(getHardware()->getSubscribeOptions(), ElementsAre(options[0]));
+
+ getHardware()->clearSubscribeOptions();
+ result = getManager()->subscribe(getCallbackClient(), options, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ ASSERT_TRUE(getHardware()->getSubscribeOptions().empty());
+
+ std::vector<SubscribeOptions> newOptions = {{
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 10.0,
+ .enableVariableUpdateRate = false,
+ }};
+ result = getManager()->subscribe(getCallbackClient(), newOptions, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ ASSERT_THAT(getHardware()->getSubscribeOptions(), ElementsAre(newOptions[0]));
+}
+
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur_filterUnchangedEvents) {
+ SpAIBinder binder1 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+ std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
+ SpAIBinder binder2 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+ std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2);
+ SubscribeOptions client1Option = {
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 10.0,
+ .enableVariableUpdateRate = false,
+ };
+ auto result = getManager()->subscribe(client1, {client1Option}, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ ASSERT_THAT(getHardware()->getSubscribeOptions(), UnorderedElementsAre(client1Option));
+
+ getHardware()->clearSubscribeOptions();
+ SubscribeOptions client2Option = {
+ .propId = 0,
+ .areaIds = {0, 1},
+ .sampleRate = 20.0,
+ .enableVariableUpdateRate = true,
+ };
+
+ result = getManager()->subscribe(client2, {client2Option}, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ ASSERT_THAT(getHardware()->getSubscribeOptions(),
+ UnorderedElementsAre(
+ SubscribeOptions{
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 20.0,
+ // This is enabled for client2, but disabled for client1.
+ .enableVariableUpdateRate = false,
+ },
+ SubscribeOptions{
+ .propId = 0,
+ .areaIds = {1},
+ .sampleRate = 20.0,
+ .enableVariableUpdateRate = true,
+ }));
+
+ std::vector<VehiclePropValue> propertyEvents = {{
+ .prop = 0,
+ .areaId = 0,
+ .value = {.int32Values = {0}},
+ .timestamp = 1,
+ },
+ {
+ .prop = 0,
+ .areaId = 1,
+ .value = {.int32Values = {1}},
+ .timestamp = 1,
+ }};
+ auto clients =
+ getManager()->getSubscribedClients(std::vector<VehiclePropValue>(propertyEvents));
+
+ ASSERT_THAT(clients[client1], UnorderedElementsAre(propertyEvents[0]));
+ ASSERT_THAT(clients[client2], UnorderedElementsAre(propertyEvents[0], propertyEvents[1]));
+
+ // If the same property events happen again with a new timestamp.
+ // VUR is disabled for client1, enabled for client2.
+ clients = getManager()->getSubscribedClients({{
+ .prop = 0,
+ .areaId = 0,
+ .value = {.int32Values = {0}},
+ .timestamp = 2,
+ }});
+
+ ASSERT_FALSE(clients.find(client1) == clients.end())
+ << "Must not filter out property events if VUR is not enabled";
+ ASSERT_TRUE(clients.find(client2) == clients.end())
+ << "Must filter out property events if VUR is enabled";
+}
+
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur_mustNotFilterStatusChange) {
+ SpAIBinder binder1 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+ std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
+ SpAIBinder binder2 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+ std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2);
+ SubscribeOptions client1Option = {
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 10.0,
+ .enableVariableUpdateRate = false,
+ };
+ auto result = getManager()->subscribe(client1, {client1Option}, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ ASSERT_THAT(getHardware()->getSubscribeOptions(), UnorderedElementsAre(client1Option));
+
+ getHardware()->clearSubscribeOptions();
+ SubscribeOptions client2Option = {
+ .propId = 0,
+ .areaIds = {0, 1},
+ .sampleRate = 20.0,
+ .enableVariableUpdateRate = true,
+ };
+
+ result = getManager()->subscribe(client2, {client2Option}, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ ASSERT_THAT(getHardware()->getSubscribeOptions(),
+ UnorderedElementsAre(
+ SubscribeOptions{
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 20.0,
+ // This is enabled for client2, but disabled for client1.
+ .enableVariableUpdateRate = false,
+ },
+ SubscribeOptions{
+ .propId = 0,
+ .areaIds = {1},
+ .sampleRate = 20.0,
+ .enableVariableUpdateRate = true,
+ }));
+
+ VehiclePropValue propValue1 = {
+ .prop = 0,
+ .areaId = 0,
+ .value = {.int32Values = {0}},
+ .timestamp = 1,
+ };
+ auto clients = getManager()->getSubscribedClients(std::vector<VehiclePropValue>({propValue1}));
+
+ ASSERT_THAT(clients[client1], UnorderedElementsAre(propValue1));
+
+ // A new event with the same value, but different status must not be filtered out.
+ VehiclePropValue propValue2 = {
+ .prop = 0,
+ .areaId = 0,
+ .value = {.int32Values = {0}},
+ .status = VehiclePropertyStatus::UNAVAILABLE,
+ .timestamp = 2,
+ };
+ clients = getManager()->getSubscribedClients({propValue2});
+
+ ASSERT_THAT(clients[client1], UnorderedElementsAre(propValue2))
+ << "Must not filter out property events that has status change";
+}
+
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur_timestampUpdated_filterOutdatedEvent) {
+ SpAIBinder binder1 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+ std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
+ SpAIBinder binder2 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+ std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2);
+ std::vector<SubscribeOptions> options = {{
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 10.0,
+ .enableVariableUpdateRate = true,
+ }};
+
+ // client1 subscribe with VUR enabled.
+ auto result = getManager()->subscribe(client1, options, true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ // Let client2 subscribe with VUR disabled so that we enabled VUR in DefaultVehicleHal layer.
+ result = getManager()->subscribe(client2,
+ {{
+ .propId = 0,
+ .areaIds = {0},
+ .sampleRate = 10.0,
+ .enableVariableUpdateRate = false,
+ }},
+ true);
+ ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+ VehiclePropValue value0 = {
+ .prop = 0,
+ .areaId = 0,
+ .value = {.int32Values = {0}},
+ .timestamp = 1,
+ };
+ auto clients = getManager()->getSubscribedClients({value0});
+
+ ASSERT_THAT(clients[client1], UnorderedElementsAre(value0));
+
+ // A new event with the same value arrived. This must update timestamp to 3.
+ VehiclePropValue value1 = {
+ .prop = 0,
+ .areaId = 0,
+ .value = {.int32Values = {0}},
+ .timestamp = 3,
+ };
+ clients = getManager()->getSubscribedClients({value1});
+
+ ASSERT_TRUE(clients.find(client1) == clients.end())
+ << "Must filter out duplicate property events if VUR is enabled";
+
+ // The latest timestamp is 3, so even though the value is not the same, this is outdated and
+ // must be ignored.
+ VehiclePropValue value2 = {
+ .prop = 0,
+ .areaId = 0,
+ .value = {.int32Values = {1}},
+ .timestamp = 2,
+ };
+ clients = getManager()->getSubscribedClients({value1});
+
+ ASSERT_TRUE(clients.find(client1) == clients.end())
+ << "Must filter out outdated property events if VUR is enabled";
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl b/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
index 714d514..b4f6850 100644
--- a/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
+++ b/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
@@ -36,5 +36,6 @@
enum VehiclePropertyGroup {
SYSTEM = 0x10000000,
VENDOR = 0x20000000,
+ BACKPORTED = 0x30000000,
MASK = 0xf0000000,
}
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
index a2cbdec..a417388 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
@@ -29,5 +29,34 @@
*/
VENDOR = 0x20000000,
+ /**
+ * Group reserved for backporting system properties introduced in a newer Android
+ * release to an older Android release.
+ *
+ * It is recommended to map the system property ID to a backported property ID by replacing the
+ * VehiclePropertyGroup, e.g. backported PERF_VEHICLE_SPEED(0x11600207) would be 0x31600207.
+ *
+ * When updated to a newer Android release where the property is defined as system properties,
+ * the backported properties must be migrated to system properties.
+ *
+ * In Android system, the backported property is treated the same as a vendor defined property
+ * with the same vendor permission model, a.k.a. Default required permission is
+ * `android.car.Car.PERMISSION_VENDOR_EXTENSION`, or customized by
+ * `SUPPORT_CUSTOMIZE_VENDOR_PERMISSION` VHAL property.
+ *
+ * Only applications with vendor permissions may access these backported properties.
+ *
+ * Vendors must also make sure this property's behavior is consistent with what is expected for
+ * the backported system property, e.g. the access mode, the change mode and the config array
+ * must be correct.
+ *
+ * When vendors define custom properties, they must use {@code VENDOR} flag, instead of
+ * {@code BACKPORTED}
+ */
+ BACKPORTED = 0x30000000,
+
+ /**
+ * The bit mask for {@code VehiclePropertyGroup}. This is not a group by itself.
+ */
MASK = 0xf0000000,
}
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index 3bb3f3a..a173a00 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -11,8 +11,6 @@
name: "android.hardware.biometrics.fingerprint-service.example",
vendor: true,
relative_install_path: "hw",
- init_rc: [":fingerprint-example.rc"],
- vintf_fragments: [":fingerprint-example.xml"],
local_include_dirs: ["include"],
srcs: [
"FakeLockoutTracker.cpp",
@@ -24,15 +22,21 @@
"Session.cpp",
"main.cpp",
],
+ stl: "c++_static",
shared_libs: [
- "libbase",
"libbinder_ndk",
+ "liblog",
+ ],
+ static_libs: [
+ "libandroid.hardware.biometrics.fingerprint.VirtualProps",
+ "libbase",
"android.hardware.biometrics.fingerprint-V3-ndk",
"android.hardware.biometrics.common-V3-ndk",
"android.hardware.biometrics.common.thread",
"android.hardware.biometrics.common.util",
+ "android.hardware.keymaster-V4-ndk",
],
- static_libs: ["libandroid.hardware.biometrics.fingerprint.VirtualProps"],
+ installable: false, // install APEX instead
}
cc_test {
@@ -143,12 +147,35 @@
vendor: true,
}
-filegroup {
+prebuilt_etc {
name: "fingerprint-example.rc",
- srcs: ["fingerprint-example.rc"],
+ src: "fingerprint-example.rc",
+ installable: false,
}
-filegroup {
+prebuilt_etc {
name: "fingerprint-example.xml",
- srcs: ["fingerprint-example.xml"],
+ src: "fingerprint-example.xml",
+ sub_dir: "vintf",
+ installable: false,
+}
+
+apex {
+ name: "com.android.hardware.biometrics.fingerprint.virtual",
+ manifest: "apex_manifest.json",
+ file_contexts: "apex_file_contexts",
+ key: "com.android.hardware.key",
+ certificate: ":com.android.hardware.certificate",
+ updatable: false,
+ vendor: true,
+
+ binaries: [
+ "android.hardware.biometrics.fingerprint-service.example",
+ ],
+ prebuilts: [
+ // init_rc
+ "fingerprint-example.rc",
+ // vintf_fragment
+ "fingerprint-example.xml",
+ ],
}
diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md
index 823cd18..4b4533a 100644
--- a/biometrics/fingerprint/aidl/default/README.md
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -11,12 +11,6 @@
following to your device's `.mk` file to include it:
```
-PRODUCT_PACKAGES_DEBUG += android.hardware.biometrics.fingerprint-service.example
-```
-
-or add the following to include it as an apex:
-
-```
PRODUCT_PACKAGES_DEBUG += com.android.hardware.biometrics.fingerprint.virtual
```
diff --git a/biometrics/fingerprint/aidl/default/apex/Android.bp b/biometrics/fingerprint/aidl/default/apex/Android.bp
deleted file mode 100644
index 75d84a9..0000000
--- a/biometrics/fingerprint/aidl/default/apex/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-apex {
- name: "com.android.hardware.biometrics.fingerprint.virtual",
- manifest: "manifest.json",
- file_contexts: "file_contexts",
- key: "com.android.hardware.key",
- certificate: ":com.android.hardware.certificate",
- updatable: false,
- vendor: true,
-
- binaries: [
- "android.hardware.biometrics.fingerprint-service.example",
- ],
- prebuilts: [
- // init_rc
- "fingerprint-example-apex.rc",
- // vintf_fragment
- "fingerprint-example-apex.xml",
- ],
-
- overrides: [
- "android.hardware.biometrics.fingerprint-service.example",
- ],
-}
-
-genrule {
- name: "gen-fingerprint-example-apex.rc",
- srcs: [":fingerprint-example.rc"],
- out: ["fingerprint-example-apex.rc"],
- cmd: "sed -e 's@/vendor/bin/@/apex/com.android.hardware.biometrics.fingerprint.virtual/bin/@' $(in) > $(out)",
-}
-
-prebuilt_etc {
- name: "fingerprint-example-apex.rc",
- src: ":gen-fingerprint-example-apex.rc",
- installable: false,
-}
-
-prebuilt_etc {
- name: "fingerprint-example-apex.xml",
- src: ":fingerprint-example.xml",
- sub_dir: "vintf",
- installable: false,
-}
diff --git a/biometrics/fingerprint/aidl/default/apex/file_contexts b/biometrics/fingerprint/aidl/default/apex_file_contexts
similarity index 100%
rename from biometrics/fingerprint/aidl/default/apex/file_contexts
rename to biometrics/fingerprint/aidl/default/apex_file_contexts
diff --git a/biometrics/fingerprint/aidl/default/apex/manifest.json b/biometrics/fingerprint/aidl/default/apex_manifest.json
similarity index 100%
rename from biometrics/fingerprint/aidl/default/apex/manifest.json
rename to biometrics/fingerprint/aidl/default/apex_manifest.json
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-example.rc b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
index ee4713c..da4ea45 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint-example.rc
+++ b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
@@ -1,4 +1,4 @@
-service vendor.fingerprint-example /vendor/bin/hw/android.hardware.biometrics.fingerprint-service.example
+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
diff --git a/broadcastradio/aidl/Android.bp b/broadcastradio/aidl/Android.bp
index 3f89029..e8bc5eb 100644
--- a/broadcastradio/aidl/Android.bp
+++ b/broadcastradio/aidl/Android.bp
@@ -43,6 +43,6 @@
imports: [],
},
],
- frozen: true,
+ frozen: false,
}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl
index fe8489c..b96def3 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl
@@ -37,8 +37,8 @@
android.hardware.broadcastradio.AmFmBandRange[] ranges;
int fmDeemphasis;
int fmRds;
- const int DEEMPHASIS_D50 = (1 << 0);
- const int DEEMPHASIS_D75 = (1 << 1);
- const int RDS = (1 << 0);
- const int RBDS = (1 << 1);
+ const int DEEMPHASIS_D50 = (1 << 0) /* 1 */;
+ const int DEEMPHASIS_D75 = (1 << 1) /* 2 */;
+ const int RDS = (1 << 0) /* 1 */;
+ const int RBDS = (1 << 1) /* 2 */;
}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl
index 98af437..d6d33bc 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl
@@ -35,6 +35,9 @@
@Backing(type="int") @JavaDerive(equals=true, toString=true) @VintfStability
enum ConfigFlag {
FORCE_MONO = 1,
+ /**
+ * @deprecated Use {link #FORCE_ANALOG_FM} instead
+ */
FORCE_ANALOG,
FORCE_DIGITAL,
RDS_AF,
@@ -43,4 +46,6 @@
DAB_FM_LINKING,
DAB_DAB_SOFT_LINKING,
DAB_FM_SOFT_LINKING,
+ FORCE_ANALOG_FM,
+ FORCE_ANALOG_AM,
}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/HdSubChannel.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/HdSubChannel.aidl
new file mode 100644
index 0000000..dd06134
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/HdSubChannel.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@Backing(type="int") @JavaDerive(equals=true, toString=true) @VintfStability
+enum HdSubChannel {
+ HD1 = 0,
+ HD2 = 1,
+ HD3 = 2,
+ HD4 = 3,
+ HD5 = 4,
+ HD6 = 5,
+ HD7 = 6,
+ HD8 = 7,
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl
index 4df272c..ed41af0 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl
@@ -47,6 +47,13 @@
DAB_FREQUENCY_KHZ,
DRMO_SERVICE_ID,
DRMO_FREQUENCY_KHZ,
- SXM_SERVICE_ID = (DRMO_FREQUENCY_KHZ + 2),
+ /**
+ * @deprecated SiriusXM Satellite Radio is not supported.
+ */
+ SXM_SERVICE_ID = (DRMO_FREQUENCY_KHZ + 2) /* 12 */,
+ /**
+ * @deprecated SiriusXM Satellite Radio is not supported.
+ */
SXM_CHANNEL,
+ HD_STATION_LOCATION,
}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl
index e02b6b1..b4a1efa 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl
@@ -50,4 +50,12 @@
String dabServiceNameShort;
String dabComponentName;
String dabComponentNameShort;
+ String genre;
+ String commentShortDescription;
+ String commentActualText;
+ String commercial;
+ String[] ufids;
+ String hdStationNameShort;
+ String hdStationNameLong;
+ int hdSubChannelsAvailable;
}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl
index b14023a..997cdd7 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl
@@ -42,10 +42,13 @@
int signalQuality;
android.hardware.broadcastradio.Metadata[] metadata;
android.hardware.broadcastradio.VendorKeyValue[] vendorInfo;
- const int FLAG_LIVE = (1 << 0);
- const int FLAG_MUTED = (1 << 1);
- const int FLAG_TRAFFIC_PROGRAM = (1 << 2);
- const int FLAG_TRAFFIC_ANNOUNCEMENT = (1 << 3);
- const int FLAG_TUNABLE = (1 << 4);
- const int FLAG_STEREO = (1 << 5);
+ const int FLAG_LIVE = (1 << 0) /* 1 */;
+ const int FLAG_MUTED = (1 << 1) /* 2 */;
+ const int FLAG_TRAFFIC_PROGRAM = (1 << 2) /* 4 */;
+ const int FLAG_TRAFFIC_ANNOUNCEMENT = (1 << 3) /* 8 */;
+ const int FLAG_TUNABLE = (1 << 4) /* 16 */;
+ const int FLAG_STEREO = (1 << 5) /* 32 */;
+ const int FLAG_SIGNAL_ACQUISITION = (1 << 6) /* 64 */;
+ const int FLAG_HD_SIS_ACQUISITION = (1 << 7) /* 128 */;
+ const int FLAG_HD_AUDIO_ACQUISITION = (1 << 8) /* 256 */;
}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl
index 8af74c7..b0fc018 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl
@@ -34,7 +34,7 @@
package android.hardware.broadcastradio;
@Backing(type="int") @JavaDerive(equals=true, toString=true) @VintfStability
enum Result {
- OK,
+ OK = 0,
INTERNAL_ERROR,
INVALID_ARGUMENTS,
INVALID_STATE,
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl
index 11da39c..ddf60e0 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl
@@ -36,10 +36,12 @@
* Forces the analog playback for the supporting radio technology.
*
* User may disable digital playback for FM HD Radio or hybrid FM/DAB with
- * this option. This is purely user choice, ie. does not reflect digital-
+ * this option. This is purely user choice, i.e. does not reflect digital-
* analog handover state managed from the HAL implementation side.
*
- * Some radio technologies may not support this, ie. DAB.
+ * Some radio technologies may not support this, i.e. DAB.
+ *
+ * @deprecated Use {link #FORCE_ANALOG_FM} instead
*/
FORCE_ANALOG,
@@ -89,4 +91,26 @@
* Enables DAB-FM soft-linking (related content).
*/
DAB_FM_SOFT_LINKING,
+
+ /**
+ * Forces the FM analog playback for the supporting radio technology.
+ *
+ * User may disable FM digital playback for FM HD Radio or hybrid FM/DAB
+ * with this option. This is purely user choice, i.e. does not reflect
+ * digital-analog handover state managed from the HAL implementation side.
+ *
+ * Some radio technologies may not support this, i.e. DAB.
+ */
+ FORCE_ANALOG_FM,
+
+ /**
+ * Forces the AM analog playback for the supporting radio technology.
+ *
+ * User may disable AM digital playback for AM HD Radio or hybrid AM/DAB
+ * with this option. This is purely user choice, i.e. does not reflect
+ * digital-analog handover state managed from the HAL implementation side.
+ *
+ * Some radio technologies may not support this, i.e. DAB.
+ */
+ FORCE_ANALOG_AM,
}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/HdSubChannel.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/HdSubChannel.aidl
new file mode 100644
index 0000000..46a3e0c
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/HdSubChannel.aidl
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.broadcastradio;
+
+/**
+ * Index of HD radio subchannel.
+ */
+@VintfStability
+@Backing(type="int")
+@JavaDerive(equals=true, toString=true)
+enum HdSubChannel {
+ /**
+ * Index of HD radio subchannel 1.
+ *
+ * <p>There are at most 8 HD radio subchannels of 1-based om HD radio standard. It is
+ * converted to 0-based index. 0 is the index of main program service (MPS). 1 to 7
+ * are indexes of additional supplemental program services (SPS).
+ */
+ HD1 = 0,
+ /**
+ * {@see HD1}
+ */
+ HD2 = 1,
+ /**
+ * {@see HD1}
+ */
+ HD3 = 2,
+ /**
+ * {@see HD1}
+ */
+ HD4 = 3,
+ /**
+ * {@see HD1}
+ */
+ HD5 = 4,
+ /**
+ * {@see HD1}
+ */
+ HD6 = 5,
+ /**
+ * {@see HD1}
+ */
+ HD7 = 6,
+ /**
+ * {@see HD1}
+ */
+ HD8 = 7,
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl
index 646c502..4a95a41 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl
@@ -154,11 +154,42 @@
/**
* 32bit primary identifier for SiriusXM Satellite Radio.
+ *
+ * @deprecated SiriusXM Satellite Radio is not supported.
*/
SXM_SERVICE_ID = DRMO_FREQUENCY_KHZ + 2,
/**
* 0-999 range
+ *
+ * @deprecated SiriusXM Satellite Radio is not supported.
*/
SXM_CHANNEL,
+
+ /**
+ * 64bit additional identifier for HD Radio representing station location.
+ *
+ * Consists of (from the LSB):
+ * - 4 bit: Bits 0:3 of altitude
+ * - 13 bit: Fractional bits of longitude
+ * - 8 bit: Integer bits of longitude
+ * - 1 bit: 0 for east and 1 for west for longitude
+ * - 1 bit: 0, representing latitude
+ * - 5 bit: pad of zeros separating longitude and latitude
+ * - 4 bit: Bits 4:7 of altitude
+ * - 13 bit: Fractional bits of latitude
+ * - 8 bit: Integer bits of latitude
+ * - 1 bit: 0 for north and 1 for south for latitude
+ * - 1 bit: 1, representing latitude
+ * - 5 bit: pad of zeros
+ *
+ * This format is defined in NRSC-5-C document: SY_IDD_1020s.
+ *
+ * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
+ * globally unique. To provide a best-effort solution, the station’s
+ * broadcast antenna containing the latitude and longitude may be carried
+ * as additional identifier and may be used by the tuner hardware to
+ * double-check tuning.
+ */
+ HD_STATION_LOCATION,
}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl
index 7769b8c..0ce967f 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl
@@ -116,4 +116,70 @@
* <p>Note: The string must be up to 8 characters long.
*/
String dabComponentNameShort;
+
+ /**
+ * Genre of the current audio piece (string)
+ *
+ * <p>(see NRSC-G200-A and id3v2.3.0 for more info)
+ */
+ String genre;
+
+ /**
+ * Short context description of comment (string)
+ *
+ * <p>Comment could relate to the current audio program content, or it might
+ * be unrelated information that the station chooses to send. It is
+ * composed of short content description and actual text (see NRSC-G200-A
+ * and id3v2.3.0 for more info).
+ */
+ String commentShortDescription;
+
+ /**
+ * Actual text of comment (string)
+ *
+ * @see #commentShortDescription
+ */
+ String commentActualText;
+
+ /**
+ * Commercial (string)
+ *
+ * <p>Commercial is application specific and generally used to facilitate the
+ * sale of products and services (see NRSC-G200-A and id3v2.3.0 for more info).
+ */
+ String commercial;
+
+ /**
+ * HD Unique File Identifiers (Array of strings)
+ *
+ * <p>Unique File Identifier (UFID) can be used to transmit an alphanumeric
+ * identifier of the current content, or of an advertised product or service
+ * (see NRSC-G200-A and id3v2.3.0 for more info).
+ */
+ String[] ufids;
+
+ /**
+ * HD short station name or HD universal short station name
+ *
+ * <p>It can be up to 12 characters (see SY_IDD_1020s for more info).
+ */
+ String hdStationNameShort;
+
+ /**
+ * HD long station name, HD station slogan or HD station message
+ *
+ * <p>(see SY_IDD_1020s for more info)
+ */
+ String hdStationNameLong;
+
+ /**
+ * Bit mask of all HD Radio subchannels available (uint8_t)
+ *
+ * <p>Bit {@link HdSubChannel#HD1} from LSB represents the availability
+ * of HD-1 subchannel (main program service, MPS). Bits
+ * {@link HdSubChannel#HD2} to {@link HdSubChannel#HD8} from LSB represent
+ * HD-2 to HD-8 subchannel (supplemental program services, SPS)
+ * respectively.
+ */
+ int hdSubChannelsAvailable;
}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
index 7632c81..d4ccd01 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
@@ -71,6 +71,23 @@
const int FLAG_STEREO = 1 << 5;
/**
+ * A signal has been acquired if this bit is set.
+ */
+
+ const int FLAG_SIGNAL_ACQUISITION = 1 << 6;
+ /**
+ * An HD Station Information Service (SIS) information is available if this
+ * bit is set.
+ */
+
+ const int FLAG_HD_SIS_ACQUISITION = 1 << 7;
+
+ /**
+ * An HD digital audio is available if this bit is set.
+ */
+ const int FLAG_HD_AUDIO_ACQUISITION = 1 << 8;
+
+ /**
* An identifier used to point at the program (primarily to tune to it).
*
* This field is required - its type field must not be set to
@@ -153,7 +170,8 @@
*
* It can be a combination of {@link #FLAG_LIVE}, {@link #FLAG_MUTED},
* {@link #FLAG_TRAFFIC_PROGRAM}, {@link #FLAG_TRAFFIC_ANNOUNCEMENT},
- * {@link #FLAG_TUNABLE}, and {@link #FLAG_STEREO}.
+ * {@link #FLAG_TUNABLE}, {@link #FLAG_STEREO}, {@link #FLAG_SIGNAL_ACQUISITION},
+ * {@link #FLAG_HD_SIS_ACQUISITION}, and {@link #FLAG_HD_AUDIO_ACQUISITION}.
*/
int infoFlags;
diff --git a/broadcastradio/aidl/default/Android.bp b/broadcastradio/aidl/default/Android.bp
index e95208c..743365a 100644
--- a/broadcastradio/aidl/default/Android.bp
+++ b/broadcastradio/aidl/default/Android.bp
@@ -26,11 +26,11 @@
cc_defaults {
name: "BroadcastRadioHalDefaults",
static_libs: [
- "android.hardware.broadcastradio@common-utils-aidl-lib",
+ "android.hardware.broadcastradio@common-utils-aidl-lib-V2",
"android.hardware.broadcastradio@common-utils-lib",
],
shared_libs: [
- "android.hardware.broadcastradio-V1-ndk",
+ "android.hardware.broadcastradio-V2-ndk",
"libbase",
"libbinder_ndk",
"liblog",
@@ -84,7 +84,7 @@
],
static_libs: [
"DefaultBroadcastRadioHal",
- "android.hardware.broadcastradio-V1-ndk",
+ "android.hardware.broadcastradio-V2-ndk",
],
srcs: [
"fuzzer.cpp",
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index 8584921..54186b0 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -16,6 +16,7 @@
#include "BroadcastRadio.h"
#include <broadcastradio-utils-aidl/Utils.h>
+#include <broadcastradio-utils-aidl/UtilsV2.h>
#include "resources.h"
#include <aidl/android/hardware/broadcastradio/IdentifierType.h>
@@ -221,7 +222,7 @@
resultToInt(Result::NOT_SUPPORTED), "selector is not supported");
}
- if (!utils::isValid(program)) {
+ if (!utils::isValidV2(program)) {
LOG(ERROR) << __func__ << ": selector is not valid: " << program.toString();
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
resultToInt(Result::INVALID_ARGUMENTS), "selector is not valid");
diff --git a/broadcastradio/aidl/default/broadcastradio-default.xml b/broadcastradio/aidl/default/broadcastradio-default.xml
index 1555822..a57b724 100644
--- a/broadcastradio/aidl/default/broadcastradio-default.xml
+++ b/broadcastradio/aidl/default/broadcastradio-default.xml
@@ -1,6 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.broadcastradio</name>
+ <version>2</version>
<fqname>IBroadcastRadio/amfm</fqname>
<fqname>IBroadcastRadio/dab</fqname>
</hal>
diff --git a/broadcastradio/aidl/vts/Android.bp b/broadcastradio/aidl/vts/Android.bp
index b60387e..87e48a9 100644
--- a/broadcastradio/aidl/vts/Android.bp
+++ b/broadcastradio/aidl/vts/Android.bp
@@ -35,8 +35,8 @@
"libxml2",
],
static_libs: [
- "android.hardware.broadcastradio-V1-ndk",
- "android.hardware.broadcastradio@common-utils-aidl-lib",
+ "android.hardware.broadcastradio-V2-ndk",
+ "android.hardware.broadcastradio@common-utils-aidl-lib-V2",
"android.hardware.broadcastradio@vts-utils-lib",
"libgmock",
],
diff --git a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
index 790d60b..72869cc 100644
--- a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
+++ b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
@@ -32,6 +32,7 @@
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <broadcastradio-utils-aidl/Utils.h>
+#include <broadcastradio-utils-aidl/UtilsV2.h>
#include <cutils/bitops.h>
#include <gmock/gmock.h>
@@ -50,7 +51,6 @@
using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
using ::ndk::ScopedAStatus;
using ::ndk::SharedRefBase;
-using ::std::string;
using ::std::vector;
using ::testing::_;
using ::testing::AnyNumber;
@@ -73,20 +73,29 @@
ConfigFlag::DAB_FM_SOFT_LINKING,
};
-void printSkipped(const string& msg) {
+constexpr int32_t kAidlVersion1 = 1;
+constexpr int32_t kAidlVersion2 = 2;
+
+void printSkipped(const std::string& msg) {
const auto testInfo = testing::UnitTest::GetInstance()->current_test_info();
LOG(INFO) << "[ SKIPPED ] " << testInfo->test_case_name() << "." << testInfo->name()
<< " with message: " << msg;
}
-bool isValidAmFmFreq(int64_t freq) {
+bool isValidAmFmFreq(int64_t freq, int aidlVersion) {
ProgramIdentifier id = bcutils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq);
- return bcutils::isValid(id);
+ if (aidlVersion == kAidlVersion1) {
+ return bcutils::isValid(id);
+ } else if (aidlVersion == kAidlVersion2) {
+ return bcutils::isValidV2(id);
+ }
+ LOG(ERROR) << "Unknown AIDL version " << aidlVersion;
+ return false;
}
-void validateRange(const AmFmBandRange& range) {
- EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
- EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
+void validateRange(const AmFmBandRange& range, int aidlVersion) {
+ EXPECT_TRUE(isValidAmFmFreq(range.lowerBound, aidlVersion));
+ EXPECT_TRUE(isValidAmFmFreq(range.upperBound, aidlVersion));
EXPECT_LT(range.lowerBound, range.upperBound);
EXPECT_GT(range.spacing, 0u);
EXPECT_EQ((range.upperBound - range.lowerBound) % range.spacing, 0u);
@@ -142,7 +151,7 @@
class TunerCallbackImpl final : public BnTunerCallback {
public:
- TunerCallbackImpl();
+ explicit TunerCallbackImpl(int32_t aidlVersion);
ScopedAStatus onTuneFailed(Result result, const ProgramSelector& selector) override;
ScopedAStatus onCurrentProgramInfoChanged(const ProgramInfo& info) override;
ScopedAStatus onProgramListUpdated(const ProgramListChunk& chunk) override;
@@ -160,6 +169,7 @@
private:
std::mutex mLock;
+ int32_t mCallbackAidlVersion;
bool mAntennaConnectionState GUARDED_BY(mLock);
ProgramInfo mCurrentProgramInfo GUARDED_BY(mLock);
bcutils::ProgramInfoSet mProgramList GUARDED_BY(mLock);
@@ -171,7 +181,7 @@
MOCK_METHOD1(onListUpdated, ScopedAStatus(const vector<Announcement>&));
};
-class BroadcastRadioHalTest : public testing::TestWithParam<string> {
+class BroadcastRadioHalTest : public testing::TestWithParam<std::string> {
protected:
void SetUp() override;
void TearDown() override;
@@ -183,14 +193,17 @@
std::shared_ptr<IBroadcastRadio> mModule;
Properties mProperties;
std::shared_ptr<TunerCallbackImpl> mCallback;
+ int32_t mAidlVersion;
};
-MATCHER_P(InfoHasId, id, string(negation ? "does not contain" : "contains") + " " + id.toString()) {
+MATCHER_P(InfoHasId, id,
+ std::string(negation ? "does not contain" : "contains") + " " + id.toString()) {
vector<int> ids = bcutils::getAllIds(arg.selector, id.type);
return ids.end() != find(ids.begin(), ids.end(), id.value);
}
-TunerCallbackImpl::TunerCallbackImpl() {
+TunerCallbackImpl::TunerCallbackImpl(int32_t aidlVersion) {
+ mCallbackAidlVersion = aidlVersion;
mAntennaConnectionState = true;
}
@@ -230,7 +243,12 @@
physically > IdentifierType::SXM_CHANNEL);
if (logically == IdentifierType::AMFM_FREQUENCY_KHZ) {
- std::optional<string> ps = bcutils::getMetadataString(info, Metadata::rdsPs);
+ std::optional<std::string> ps;
+ if (mCallbackAidlVersion == kAidlVersion1) {
+ ps = bcutils::getMetadataString(info, Metadata::rdsPs);
+ } else {
+ ps = bcutils::getMetadataStringV2(info, Metadata::rdsPs);
+ }
if (ps.has_value()) {
EXPECT_NE(::android::base::Trim(*ps), "")
<< "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
@@ -323,9 +341,13 @@
EXPECT_FALSE(mProperties.product.empty());
EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
- mCallback = SharedRefBase::make<TunerCallbackImpl>();
+ // get AIDL HAL version
+ ASSERT_TRUE(mModule->getInterfaceVersion(&mAidlVersion).isOk());
+ EXPECT_GE(mAidlVersion, kAidlVersion1);
+ EXPECT_LE(mAidlVersion, kAidlVersion2);
// set callback
+ mCallback = SharedRefBase::make<TunerCallbackImpl>(mAidlVersion);
EXPECT_TRUE(mModule->setTunerCallback(mCallback).isOk());
}
@@ -443,7 +465,7 @@
EXPECT_GT(config.ranges.size(), 0u);
for (const auto& range : config.ranges) {
- validateRange(range);
+ validateRange(range, mAidlVersion);
EXPECT_EQ(range.seekSpacing % range.spacing, 0u);
EXPECT_GE(range.seekSpacing, range.spacing);
}
@@ -494,7 +516,7 @@
EXPECT_GT(config.ranges.size(), 0u);
for (const auto& range : config.ranges) {
- validateRange(range);
+ validateRange(range, mAidlVersion);
EXPECT_EQ(range.seekSpacing, 0u);
}
}
@@ -522,11 +544,17 @@
std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
for (const auto& entry : config) {
- EXPECT_TRUE(std::regex_match(string(entry.label), re));
+ EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
ProgramIdentifier id =
bcutils::makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, entry.frequencyKhz);
- EXPECT_TRUE(bcutils::isValid(id));
+ if (mAidlVersion == kAidlVersion1) {
+ EXPECT_TRUE(bcutils::isValid(id));
+ } else if (mAidlVersion == kAidlVersion2) {
+ EXPECT_TRUE(bcutils::isValidV2(id));
+ } else {
+ LOG(ERROR) << "Unknown callback AIDL version " << mAidlVersion;
+ }
}
}
@@ -1175,10 +1203,21 @@
continue;
}
- std::optional<string> name = bcutils::getMetadataString(program, Metadata::programName);
- if (!name) {
- name = bcutils::getMetadataString(program, Metadata::rdsPs);
+ std::optional<std::string> name;
+ if (mAidlVersion == kAidlVersion1) {
+ name = bcutils::getMetadataString(program, Metadata::programName);
+ if (!name) {
+ name = bcutils::getMetadataString(program, Metadata::rdsPs);
+ }
+ } else if (mAidlVersion == kAidlVersion2) {
+ name = bcutils::getMetadataStringV2(program, Metadata::programName);
+ if (!name) {
+ name = bcutils::getMetadataStringV2(program, Metadata::rdsPs);
+ }
+ } else {
+ LOG(ERROR) << "Unknown HAL AIDL version " << mAidlVersion;
}
+
ASSERT_TRUE(name.has_value());
ProgramIdentifier expectedId = bcutils::makeHdRadioStationName(*name);
diff --git a/broadcastradio/common/utilsaidl/Android.bp b/broadcastradio/common/utilsaidl/Android.bp
index fa6de19..4ec635b 100644
--- a/broadcastradio/common/utilsaidl/Android.bp
+++ b/broadcastradio/common/utilsaidl/Android.bp
@@ -25,6 +25,29 @@
cc_library_static {
name: "android.hardware.broadcastradio@common-utils-aidl-lib",
+ defaults: [
+ "VtsBroadcastRadioDefaults",
+ ],
+ shared_libs: [
+ "android.hardware.broadcastradio-V1-ndk",
+ ],
+}
+
+cc_library_static {
+ name: "android.hardware.broadcastradio@common-utils-aidl-lib-V2",
+ defaults: [
+ "VtsBroadcastRadioDefaults",
+ ],
+ srcs: [
+ "src/UtilsV2.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.broadcastradio-V2-ndk",
+ ],
+}
+
+cc_defaults {
+ name: "VtsBroadcastRadioDefaults",
vendor_available: true,
relative_install_path: "hw",
cflags: [
@@ -37,11 +60,10 @@
"-std=c++1z",
],
srcs: [
- "Utils.cpp",
+ "src/Utils.cpp",
],
export_include_dirs: ["include"],
shared_libs: [
- "android.hardware.broadcastradio-V1-ndk",
"libbase",
],
static_libs: [
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
index ee85a17..b6fb33f 100644
--- a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
@@ -17,7 +17,6 @@
#pragma once
#include <aidl/android/hardware/broadcastradio/IdentifierType.h>
-#include <aidl/android/hardware/broadcastradio/Metadata.h>
#include <aidl/android/hardware/broadcastradio/ProgramFilter.h>
#include <aidl/android/hardware/broadcastradio/ProgramIdentifier.h>
#include <aidl/android/hardware/broadcastradio/ProgramInfo.h>
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/UtilsV2.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/UtilsV2.h
new file mode 100644
index 0000000..e411aa4
--- /dev/null
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/UtilsV2.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/broadcastradio/IdentifierType.h>
+#include <aidl/android/hardware/broadcastradio/Metadata.h>
+#include <aidl/android/hardware/broadcastradio/ProgramIdentifier.h>
+#include <aidl/android/hardware/broadcastradio/ProgramInfo.h>
+#include <aidl/android/hardware/broadcastradio/ProgramSelector.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+namespace utils {
+
+bool isValidV2(const ProgramIdentifier& id);
+bool isValidV2(const ProgramSelector& sel);
+std::optional<std::string> getMetadataStringV2(const ProgramInfo& info, const Metadata::Tag& tag);
+
+} // namespace utils
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/common/utilsaidl/Utils.cpp b/broadcastradio/common/utilsaidl/src/Utils.cpp
similarity index 95%
rename from broadcastradio/common/utilsaidl/Utils.cpp
rename to broadcastradio/common/utilsaidl/src/Utils.cpp
index de4f529..2875318 100644
--- a/broadcastradio/common/utilsaidl/Utils.cpp
+++ b/broadcastradio/common/utilsaidl/src/Utils.cpp
@@ -31,7 +31,6 @@
namespace {
using ::android::base::EqualsIgnoreCase;
-using ::std::string;
using ::std::vector;
const int64_t kValueForNotFoundIdentifier = 0;
@@ -207,7 +206,7 @@
uint64_t val = static_cast<uint64_t>(id.value);
bool valid = true;
- auto expect = [&valid](bool condition, const string& message) {
+ auto expect = [&valid](bool condition, const std::string& message) {
if (!condition) {
valid = false;
LOG(ERROR) << "identifier not valid, expected " << message;
@@ -278,9 +277,9 @@
case IdentifierType::SXM_CHANNEL:
expect(val < 1000u, "SXM channel < 1000");
break;
- case IdentifierType::VENDOR_START:
- case IdentifierType::VENDOR_END:
- // skip
+ default:
+ expect(id.type >= IdentifierType::VENDOR_START && id.type <= IdentifierType::VENDOR_END,
+ "Undefined identifier type");
break;
}
@@ -452,10 +451,10 @@
return metadataString;
}
-ProgramIdentifier makeHdRadioStationName(const string& name) {
+ProgramIdentifier makeHdRadioStationName(const std::string& name) {
constexpr size_t maxlen = 8;
- string shortName;
+ std::string shortName;
shortName.reserve(maxlen);
const auto& loc = std::locale::classic();
@@ -484,7 +483,7 @@
return static_cast<IdentifierType>(typeAsInt);
}
-bool parseArgInt(const string& s, int* out) {
+bool parseArgInt(const std::string& s, int* out) {
return ::android::base::ParseInt(s, out);
}
@@ -492,7 +491,7 @@
return ::android::base::ParseInt(s, out);
}
-bool parseArgBool(const string& s, bool* out) {
+bool parseArgBool(const std::string& s, bool* out) {
if (EqualsIgnoreCase(s, "true")) {
*out = true;
} else if (EqualsIgnoreCase(s, "false")) {
@@ -503,7 +502,7 @@
return true;
}
-bool parseArgDirection(const string& s, bool* out) {
+bool parseArgDirection(const std::string& s, bool* out) {
if (EqualsIgnoreCase(s, "up")) {
*out = true;
} else if (EqualsIgnoreCase(s, "down")) {
@@ -514,8 +513,8 @@
return true;
}
-bool parseArgIdentifierTypeArray(const string& s, vector<IdentifierType>* out) {
- for (const string& val : ::android::base::Split(s, ",")) {
+bool parseArgIdentifierTypeArray(const std::string& s, vector<IdentifierType>* out) {
+ for (const std::string& val : ::android::base::Split(s, ",")) {
int outInt;
if (!parseArgInt(val, &outInt)) {
return false;
@@ -526,8 +525,8 @@
}
bool parseProgramIdentifierList(const std::string& s, vector<ProgramIdentifier>* out) {
- for (const string& idStr : ::android::base::Split(s, ",")) {
- const vector<string> idStrPair = ::android::base::Split(idStr, ":");
+ for (const std::string& idStr : ::android::base::Split(s, ",")) {
+ const vector<std::string> idStrPair = ::android::base::Split(idStr, ":");
if (idStrPair.size() != 2) {
return false;
}
diff --git a/broadcastradio/common/utilsaidl/src/UtilsV2.cpp b/broadcastradio/common/utilsaidl/src/UtilsV2.cpp
new file mode 100644
index 0000000..ef739df
--- /dev/null
+++ b/broadcastradio/common/utilsaidl/src/UtilsV2.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BcRadioAidlDef.utilsV2"
+
+#include "broadcastradio-utils-aidl/UtilsV2.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+namespace utils {
+
+bool isValidV2(const ProgramIdentifier& id) {
+ uint64_t val = static_cast<uint64_t>(id.value);
+ bool valid = true;
+
+ auto expect = [&valid](bool condition, const std::string& message) {
+ if (!condition) {
+ valid = false;
+ LOG(ERROR) << "identifier not valid, expected " << message;
+ }
+ };
+
+ switch (id.type) {
+ case IdentifierType::INVALID:
+ expect(false, "IdentifierType::INVALID");
+ break;
+ case IdentifierType::DAB_FREQUENCY_KHZ:
+ expect(val > 100000u, "f > 100MHz");
+ [[fallthrough]];
+ case IdentifierType::AMFM_FREQUENCY_KHZ:
+ case IdentifierType::DRMO_FREQUENCY_KHZ:
+ expect(val > 100u, "f > 100kHz");
+ expect(val < 10000000u, "f < 10GHz");
+ break;
+ case IdentifierType::RDS_PI:
+ expect(val != 0u, "RDS PI != 0");
+ expect(val <= 0xFFFFu, "16bit id");
+ break;
+ case IdentifierType::HD_STATION_ID_EXT: {
+ uint64_t stationId = val & 0xFFFFFFFF; // 32bit
+ val >>= 32;
+ uint64_t subchannel = val & 0xF; // 4bit
+ val >>= 4;
+ uint64_t freq = val & 0x3FFFF; // 18bit
+ expect(stationId != 0u, "HD station id != 0");
+ expect(subchannel < 8u, "HD subch < 8");
+ expect(freq > 100u, "f > 100kHz");
+ expect(freq < 10000000u, "f < 10GHz");
+ break;
+ }
+ case IdentifierType::HD_STATION_NAME: {
+ while (val > 0) {
+ char ch = static_cast<char>(val & 0xFF);
+ val >>= 8;
+ expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
+ "HD_STATION_NAME does not match [A-Z0-9]+");
+ }
+ break;
+ }
+ case IdentifierType::DAB_SID_EXT: {
+ uint64_t sid = val & 0xFFFFFFFF; // 32bit
+ val >>= 32;
+ uint64_t ecc = val & 0xFF; // 8bit
+ expect(sid != 0u, "DAB SId != 0");
+ expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
+ break;
+ }
+ case IdentifierType::DAB_ENSEMBLE:
+ expect(val != 0u, "DAB ensemble != 0");
+ expect(val <= 0xFFFFu, "16bit id");
+ break;
+ case IdentifierType::DAB_SCID:
+ expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
+ expect(val <= 0xFFFu, "12bit id");
+ break;
+ case IdentifierType::DRMO_SERVICE_ID:
+ expect(val != 0u, "DRM SId != 0");
+ expect(val <= 0xFFFFFFu, "24bit id");
+ break;
+ case IdentifierType::SXM_SERVICE_ID:
+ expect(val != 0u, "SXM SId != 0");
+ expect(val <= 0xFFFFFFFFu, "32bit id");
+ break;
+ case IdentifierType::SXM_CHANNEL:
+ expect(val < 1000u, "SXM channel < 1000");
+ break;
+ case IdentifierType::HD_STATION_LOCATION: {
+ uint64_t latitudeBit = val & 0x1;
+ expect(latitudeBit == 1u, "Latitude comes first");
+ val >>= 27;
+ uint64_t latitudePad = val & 0x1Fu;
+ expect(latitudePad == 0u, "Latitude padding");
+ val >>= 5;
+ uint64_t longitudeBit = val & 0x1;
+ expect(longitudeBit == 1u, "Longitude comes next");
+ val >>= 27;
+ uint64_t longitudePad = val & 0x1Fu;
+ expect(longitudePad == 0u, "Latitude padding");
+ break;
+ }
+ default:
+ expect(id.type >= IdentifierType::VENDOR_START && id.type <= IdentifierType::VENDOR_END,
+ "Undefined identifier type");
+ break;
+ }
+
+ return valid;
+}
+
+bool isValidV2(const ProgramSelector& sel) {
+ if (sel.primaryId.type != IdentifierType::AMFM_FREQUENCY_KHZ &&
+ sel.primaryId.type != IdentifierType::RDS_PI &&
+ sel.primaryId.type != IdentifierType::HD_STATION_ID_EXT &&
+ sel.primaryId.type != IdentifierType::DAB_SID_EXT &&
+ sel.primaryId.type != IdentifierType::DRMO_SERVICE_ID &&
+ sel.primaryId.type != IdentifierType::SXM_SERVICE_ID &&
+ (sel.primaryId.type < IdentifierType::VENDOR_START ||
+ sel.primaryId.type > IdentifierType::VENDOR_END)) {
+ return false;
+ }
+ return isValidV2(sel.primaryId);
+}
+
+std::optional<std::string> getMetadataStringV2(const ProgramInfo& info, const Metadata::Tag& tag) {
+ auto isRdsPs = [tag](const Metadata& item) { return item.getTag() == tag; };
+
+ auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isRdsPs);
+ if (it == info.metadata.end()) {
+ return std::nullopt;
+ }
+
+ std::string metadataString;
+ switch (it->getTag()) {
+ case Metadata::rdsPs:
+ metadataString = it->get<Metadata::rdsPs>();
+ break;
+ case Metadata::rdsPty:
+ metadataString = std::to_string(it->get<Metadata::rdsPty>());
+ break;
+ case Metadata::rbdsPty:
+ metadataString = std::to_string(it->get<Metadata::rbdsPty>());
+ break;
+ case Metadata::rdsRt:
+ metadataString = it->get<Metadata::rdsRt>();
+ break;
+ case Metadata::songTitle:
+ metadataString = it->get<Metadata::songTitle>();
+ break;
+ case Metadata::songArtist:
+ metadataString = it->get<Metadata::songArtist>();
+ break;
+ case Metadata::songAlbum:
+ metadataString = it->get<Metadata::songAlbum>();
+ break;
+ case Metadata::stationIcon:
+ metadataString = std::to_string(it->get<Metadata::stationIcon>());
+ break;
+ case Metadata::albumArt:
+ metadataString = std::to_string(it->get<Metadata::albumArt>());
+ break;
+ case Metadata::programName:
+ metadataString = it->get<Metadata::programName>();
+ break;
+ case Metadata::dabEnsembleName:
+ metadataString = it->get<Metadata::dabEnsembleName>();
+ break;
+ case Metadata::dabEnsembleNameShort:
+ metadataString = it->get<Metadata::dabEnsembleNameShort>();
+ break;
+ case Metadata::dabServiceName:
+ metadataString = it->get<Metadata::dabServiceName>();
+ break;
+ case Metadata::dabServiceNameShort:
+ metadataString = it->get<Metadata::dabServiceNameShort>();
+ break;
+ case Metadata::dabComponentName:
+ metadataString = it->get<Metadata::dabComponentName>();
+ break;
+ case Metadata::dabComponentNameShort:
+ metadataString = it->get<Metadata::dabComponentNameShort>();
+ break;
+ case Metadata::genre:
+ metadataString = it->get<Metadata::genre>();
+ break;
+ case Metadata::commentShortDescription:
+ metadataString = it->get<Metadata::commentShortDescription>();
+ break;
+ case Metadata::commentActualText:
+ metadataString = it->get<Metadata::commentActualText>();
+ break;
+ case Metadata::commercial:
+ metadataString = it->get<Metadata::commercial>();
+ break;
+ case Metadata::ufids: {
+ auto& ufids = it->get<Metadata::ufids>();
+ metadataString = "[";
+ for (const auto& ufid : ufids) {
+ metadataString += std::string(ufid) + ",";
+ }
+ if (ufids.empty()) {
+ metadataString += "]";
+ } else {
+ metadataString[metadataString.size() - 1] = ']';
+ }
+ } break;
+ case Metadata::hdStationNameShort:
+ metadataString = it->get<Metadata::hdStationNameShort>();
+ break;
+ case Metadata::hdStationNameLong:
+ metadataString = it->get<Metadata::hdStationNameLong>();
+ break;
+ case Metadata::hdSubChannelsAvailable:
+ metadataString = std::to_string(it->get<Metadata::hdSubChannelsAvailable>());
+ break;
+ default:
+ LOG(ERROR) << "Metadata " << it->toString() << " is not converted.";
+ return std::nullopt;
+ }
+ return metadataString;
+}
+
+} // namespace utils
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/cas/aidl/default/Android.bp b/cas/aidl/default/Android.bp
index 34ecbf2..06e167c 100644
--- a/cas/aidl/default/Android.bp
+++ b/cas/aidl/default/Android.bp
@@ -80,7 +80,8 @@
cc_fuzz {
name: "android.hardware.cas-service_fuzzer",
- vendor: true,
+ // TODO(b/307611931): avoid fuzzing on vendor until hermiticity issue is fixed
+ // vendor: true,
defaults: ["service_fuzzer_defaults"],
srcs: ["fuzzer.cpp"],
diff --git a/compatibility_matrices/compatibility_matrix.9.xml b/compatibility_matrices/compatibility_matrix.9.xml
index 75915e3..6bda4a1 100644
--- a/compatibility_matrices/compatibility_matrix.9.xml
+++ b/compatibility_matrices/compatibility_matrix.9.xml
@@ -157,6 +157,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.broadcastradio</name>
+ <version>1-2</version>
<interface>
<name>IBroadcastRadio</name>
<regex-instance>.*</regex-instance>
@@ -240,15 +241,6 @@
<instance>default</instance>
</interface>
</hal>
- <!-- Either the native or the HIDL mapper HAL must exist on the device -->
- <hal format="hidl" optional="true">
- <name>android.hardware.graphics.mapper</name>
- <version>4.0</version>
- <interface>
- <name>IMapper</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.health</name>
<version>1-2</version>
@@ -511,6 +503,14 @@
</interface>
</hal>
<hal format="aidl" optional="true" updatable-via-apex="true">
+ <name>android.hardware.security.authgraph</name>
+ <version>1</version>
+ <interface>
+ <name>IAuthGraphKeyExchange</name>
+ <instance>nonsecure</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="true" updatable-via-apex="true">
<name>android.hardware.security.secureclock</name>
<version>1</version>
<interface>
@@ -678,7 +678,7 @@
<instance>default</instance>
</interface>
</hal>
- <!-- Either the native or the HIDL mapper HAL must exist on the device -->
+ <!-- The native mapper HAL must exist on the device -->
<hal format="native" optional="true">
<name>mapper</name>
<version>5.0</version>
diff --git a/gatekeeper/aidl/Android.bp b/gatekeeper/aidl/Android.bp
index b050f6a..169a7d5 100644
--- a/gatekeeper/aidl/Android.bp
+++ b/gatekeeper/aidl/Android.bp
@@ -25,6 +25,9 @@
cpp: {
enabled: false,
},
+ rust: {
+ enabled: true,
+ },
},
versions_with_info: [
{
diff --git a/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl b/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
index c66d9e8..69de750 100644
--- a/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
+++ b/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
@@ -46,27 +46,28 @@
double carrierFrequencyHz;
/**
- * GNSS signal code type "A" representing GALILEO E1A, GALILEO E6A, IRNSS L5A SPS, IRNSS SA SPS,
+ * GNSS signal code type "A" representing GALILEO E1A, GALILEO E6A, NavIC L5A SPS, NavIC SA SPS,
* GLONASS G1a L1OCd, GLONASS G2a L2CSI.
*/
const @utf8InCpp String CODE_TYPE_A = "A";
/**
- * GNSS signal code type "B" representing GALILEO E1B, GALILEO E6B, IRNSS L5B RS (D),
- * IRNSS SB RS (D), GLONASS G1a L1OCp, GLONASS G2a L2OCp, QZSS L1Sb.
+ * GNSS signal code type "B" representing GALILEO E1B, GALILEO E6B, NavIC L5B RS (D),
+ * NavIC SB RS (D), GLONASS G1a L1OCp, GLONASS G2a L2OCp, QZSS L1Sb.
*/
const @utf8InCpp String CODE_TYPE_B = "B";
/**
* GNSS signal code type "C" representing GPS L1 C/A, GPS L2 C/A, GLONASS G1 C/A,
- * GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C RS (P),
- * IRNSS SC RS (P).
+ * GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, NavIC L5C RS (P),
+ * NavIC SC RS (P).
*/
const @utf8InCpp String CODE_TYPE_C = "C";
/**
* GNSS signal code type "D" representing GPS L2 (L1(C/A) + (P2-P1) (semi-codeless)),
- * QZSS L5S(I), BDS B1C Data, BDS B2a Data, BDS B2b Data, BDS B2 (B2a+B2b) Data, BDS B3a Data.
+ * QZSS L5S(I), BDS B1C Data, BDS B2a Data, BDS B2b Data, BDS B2 (B2a+B2b) Data, BDS B3a Data,
+ * NavIC L1 Data.
*/
const @utf8InCpp String CODE_TYPE_D = "D";
@@ -100,7 +101,7 @@
/**
* GNSS signal code type "P" representing GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P,
* BDS B1C Pilot, BDS B2a Pilot, BDS B2b Pilot, BDS B2 (B2a+B2b) Pilot, BDS B3a Pilot,
- * QZSS L5S(Q).
+ * QZSS L5S(Q), NavIC L1 Pilot.
*/
const @utf8InCpp String CODE_TYPE_P = "P";
@@ -127,7 +128,7 @@
* GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO E5a+b (I+Q), GALILEO E6 (B+C), SBAS L5 (I+Q),
* QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q), QZSS L6 (D+P), BDS B1 (I+Q),
* BDS B1C Data+Pilot, BDS B2a Data+Pilot, BDS B2 (I+Q), BDS B2 (B2a+B2b) Data+Pilot,
- * BDS B3 (I+Q), IRNSS L5 (B+C), IRNSS S (B+C).
+ * BDS B3 (I+Q), NavIC L5 (B+C), NavIC S (B+C), NavIC L1 Data+Pilot.
*/
const @utf8InCpp String CODE_TYPE_X = "X";
diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp
index f1b9cbf..c31f991 100644
--- a/gnss/aidl/default/Gnss.cpp
+++ b/gnss/aidl/default/Gnss.cpp
@@ -115,7 +115,9 @@
mGnssMeasurementInterface->setLocationEnabled(true);
this->reportGnssStatusValue(IGnssCallback::GnssStatusValue::SESSION_BEGIN);
mThread = std::thread([this]() {
- this->reportSvStatus();
+ if (!mGnssMeasurementEnabled || mMinIntervalMs <= mGnssMeasurementIntervalMs) {
+ this->reportSvStatus();
+ }
if (!mFirstFixReceived) {
std::this_thread::sleep_for(std::chrono::milliseconds(TTFF_MILLIS));
mFirstFixReceived = true;
@@ -124,7 +126,9 @@
if (!mIsActive) {
break;
}
- this->reportSvStatus();
+ if (!mGnssMeasurementEnabled || mMinIntervalMs <= mGnssMeasurementIntervalMs) {
+ this->reportSvStatus();
+ }
this->reportNmea();
auto currentLocation = getLocationFromHW();
@@ -386,4 +390,12 @@
return ndk::ScopedAStatus::ok();
}
+void Gnss::setGnssMeasurementEnabled(const bool enabled) {
+ mGnssMeasurementEnabled = enabled;
+}
+
+void Gnss::setGnssMeasurementInterval(const long intervalMs) {
+ mGnssMeasurementIntervalMs = intervalMs;
+}
+
} // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/Gnss.h b/gnss/aidl/default/Gnss.h
index 00540cd..245d607 100644
--- a/gnss/aidl/default/Gnss.h
+++ b/gnss/aidl/default/Gnss.h
@@ -85,6 +85,8 @@
override;
void reportSvStatus() const;
+ void setGnssMeasurementEnabled(const bool enabled);
+ void setGnssMeasurementInterval(const long intervalMs);
std::shared_ptr<GnssConfiguration> mGnssConfiguration;
std::shared_ptr<GnssPowerIndication> mGnssPowerIndication;
std::shared_ptr<GnssMeasurementInterface> mGnssMeasurementInterface;
@@ -101,10 +103,12 @@
static std::shared_ptr<IGnssCallback> sGnssCallback;
std::atomic<long> mMinIntervalMs;
+ std::atomic<long> mGnssMeasurementIntervalMs;
std::atomic<bool> mIsActive;
std::atomic<bool> mIsSvStatusActive;
std::atomic<bool> mIsNmeaActive;
std::atomic<bool> mFirstFixReceived;
+ std::atomic<bool> mGnssMeasurementEnabled;
std::thread mThread;
::android::hardware::gnss::common::ThreadBlocker mThreadBlocker;
diff --git a/gnss/aidl/default/GnssMeasurementInterface.cpp b/gnss/aidl/default/GnssMeasurementInterface.cpp
index f7e4d4a..f324213 100644
--- a/gnss/aidl/default/GnssMeasurementInterface.cpp
+++ b/gnss/aidl/default/GnssMeasurementInterface.cpp
@@ -76,6 +76,7 @@
stop();
}
mIntervalMs = std::max(options.intervalMs, 1000);
+ mGnss->setGnssMeasurementInterval(mIntervalMs);
start(options.enableCorrVecOutputs, options.enableFullTracking);
return ndk::ScopedAStatus::ok();
@@ -104,6 +105,7 @@
}
mIsActive = true;
+ mGnss->setGnssMeasurementEnabled(true);
mThreads.emplace_back(std::thread([this, enableCorrVecOutputs, enableFullTracking]() {
waitForStoppingThreads();
mThreadBlocker.reset();
@@ -129,7 +131,7 @@
auto measurement =
Utils::getMockMeasurement(enableCorrVecOutputs, enableFullTracking);
this->reportMeasurement(measurement);
- if (!mLocationEnabled) {
+ if (!mLocationEnabled || mLocationIntervalMs > mIntervalMs) {
mGnss->reportSvStatus();
}
}
@@ -142,6 +144,7 @@
void GnssMeasurementInterface::stop() {
ALOGD("stop");
mIsActive = false;
+ mGnss->setGnssMeasurementEnabled(false);
mThreadBlocker.notify();
for (auto iter = mThreads.begin(); iter != mThreads.end(); ++iter) {
if (iter->joinable()) {
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index aa8bdfd..9381a0a 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -1430,13 +1430,13 @@
startMeasurementWithInterval(intervals[i], iGnssMeasurement, callback);
std::vector<int> measurementDeltas;
- std::vector<int> svInfoListTimestampsDeltas;
+ std::vector<int> svInfoListDeltas;
collectMeasurementIntervals(callback, numEvents[i], /* timeoutSeconds= */ 10,
measurementDeltas);
if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
- collectSvInfoListTimestamps(numEvents[i], /* timeoutSeconds= */ 10,
- svInfoListTimestampsDeltas);
+ collectSvInfoListTimestamps(numEvents[i], /* timeoutSeconds= */ 10, svInfoListDeltas);
+ EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
}
status = iGnssMeasurement->close();
ASSERT_TRUE(status.isOk());
@@ -1444,8 +1444,7 @@
assertMeanAndStdev(intervals[i], measurementDeltas);
if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
- assertMeanAndStdev(intervals[i], svInfoListTimestampsDeltas);
- EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+ assertMeanAndStdev(intervals[i], svInfoListDeltas);
}
}
}
@@ -1477,13 +1476,25 @@
auto callback = sp<GnssMeasurementCallbackAidl>::make();
startMeasurementWithInterval(intervalMs, iGnssMeasurement, callback);
- std::vector<int> deltas;
- collectMeasurementIntervals(callback, /*numEvents=*/10, /*timeoutSeconds=*/10, deltas);
+ std::vector<int> measurementDeltas;
+ std::vector<int> svInfoListDeltas;
+
+ collectMeasurementIntervals(callback, /*numEvents=*/10, /*timeoutSeconds=*/10,
+ measurementDeltas);
+ if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+ collectSvInfoListTimestamps(/*numEvents=*/10, /* timeoutSeconds= */ 10,
+ svInfoListDeltas);
+ EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+ }
status = iGnssMeasurement->close();
ASSERT_TRUE(status.isOk());
- assertMeanAndStdev(locationIntervalMs, deltas);
+ assertMeanAndStdev(locationIntervalMs, measurementDeltas);
+ if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+ // Verify the SvStatus interval is 1s (not 2s)
+ assertMeanAndStdev(locationIntervalMs, svInfoListDeltas);
+ }
}
StopAndClearLocations();
}
@@ -1516,16 +1527,37 @@
// Start location and verify the measurements are received at 1Hz
StartAndCheckFirstLocation(locationIntervalMs, /* lowPowerMode= */ false);
- std::vector<int> deltas;
- collectMeasurementIntervals(callback, /*numEvents=*/10, kFirstMeasTimeoutSec, deltas);
- assertMeanAndStdev(locationIntervalMs, deltas);
+ std::vector<int> measurementDeltas;
+ std::vector<int> svInfoListDeltas;
+ collectMeasurementIntervals(callback, /*numEvents=*/10, kFirstMeasTimeoutSec,
+ measurementDeltas);
+ assertMeanAndStdev(locationIntervalMs, measurementDeltas);
+ if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+ collectSvInfoListTimestamps(/*numEvents=*/10, /* timeoutSeconds= */ 10,
+ svInfoListDeltas);
+ EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+ // Verify the SvStatus intervals are at 1s interval
+ assertMeanAndStdev(locationIntervalMs, svInfoListDeltas);
+ }
// Stop location request and verify the measurements are received at 2s intervals
StopAndClearLocations();
- callback->gnss_data_cbq_.reset();
- deltas.clear();
- collectMeasurementIntervals(callback, /*numEvents=*/5, kFirstMeasTimeoutSec, deltas);
- assertMeanAndStdev(intervalMs, deltas);
+ measurementDeltas.clear();
+ collectMeasurementIntervals(callback, /*numEvents=*/5, kFirstMeasTimeoutSec,
+ measurementDeltas);
+ assertMeanAndStdev(intervalMs, measurementDeltas);
+
+ if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+ svInfoListDeltas.clear();
+ collectSvInfoListTimestamps(/*numEvents=*/5, /* timeoutSeconds= */ 10,
+ svInfoListDeltas);
+ EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+ // Verify the SvStatus intervals are at 2s interval
+ for (const int& delta : svInfoListDeltas) {
+ ALOGD("svInfoListDelta: %d", delta);
+ }
+ assertMeanAndStdev(intervalMs, svInfoListDeltas);
+ }
status = iGnssMeasurement->close();
ASSERT_TRUE(status.isOk());
@@ -1587,8 +1619,7 @@
* TestGnssMeasurementIsFullTracking
* 1. Start measurement with enableFullTracking=true. Verify the received measurements have
* isFullTracking=true.
- * 2. Start measurement with enableFullTracking = false. Verify the received measurements have
- * isFullTracking=false.
+ * 2. Start measurement with enableFullTracking = false.
* 3. Do step 1 again.
*/
TEST_P(GnssHalTest, TestGnssMeasurementIsFullTracking) {
@@ -1675,4 +1706,59 @@
ASSERT_TRUE(accumulatedDeltaRangeFound);
status = iGnssMeasurement->close();
ASSERT_TRUE(status.isOk());
-}
\ No newline at end of file
+}
+
+/*
+ * TestSvStatusIntervals:
+ * 1. start measurement and location with various intervals
+ * 2. verify the SvStatus are received at expected interval
+ */
+TEST_P(GnssHalTest, TestSvStatusIntervals) {
+ if (aidl_gnss_hal_->getInterfaceVersion() <= 2) {
+ return;
+ }
+ ALOGD("TestSvStatusIntervals");
+ sp<IGnssMeasurementInterface> iGnssMeasurement;
+ auto status = aidl_gnss_hal_->getExtensionGnssMeasurement(&iGnssMeasurement);
+ ASSERT_TRUE(status.isOk());
+ ASSERT_TRUE(iGnssMeasurement != nullptr);
+
+ std::vector<int> locationIntervals{1000, 2000, INT_MAX};
+ std::vector<int> measurementIntervals{1000, 2000, INT_MAX};
+
+ for (auto& locationIntervalMs : locationIntervals) {
+ for (auto& measurementIntervalMs : measurementIntervals) {
+ if (locationIntervalMs == INT_MAX && measurementIntervalMs == INT_MAX) {
+ continue;
+ }
+ auto measurementCallback = sp<GnssMeasurementCallbackAidl>::make();
+ // Start measurement
+ if (measurementIntervalMs < INT_MAX) {
+ startMeasurementWithInterval(measurementIntervalMs, iGnssMeasurement,
+ measurementCallback);
+ }
+ // Start location
+ if (locationIntervalMs < INT_MAX) {
+ StartAndCheckFirstLocation(locationIntervalMs, /* lowPowerMode= */ false);
+ }
+ ALOGD("location@%d(ms), measurement@%d(ms)", locationIntervalMs, measurementIntervalMs);
+ std::vector<int> svInfoListDeltas;
+ collectSvInfoListTimestamps(/*numEvents=*/5, /* timeoutSeconds= */ 10,
+ svInfoListDeltas);
+ EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+
+ int svStatusInterval = std::min(locationIntervalMs, measurementIntervalMs);
+ assertMeanAndStdev(svStatusInterval, svInfoListDeltas);
+
+ if (locationIntervalMs < INT_MAX) {
+ // Stop location request
+ StopAndClearLocations();
+ }
+ if (measurementIntervalMs < INT_MAX) {
+ // Stop measurement request
+ status = iGnssMeasurement->close();
+ ASSERT_TRUE(status.isOk());
+ }
+ }
+ }
+}
diff --git a/keymaster/4.0/support/fuzzer/keymaster4_utils_fuzzer.cpp b/keymaster/4.0/support/fuzzer/keymaster4_utils_fuzzer.cpp
index bf074e8..55c2630 100644
--- a/keymaster/4.0/support/fuzzer/keymaster4_utils_fuzzer.cpp
+++ b/keymaster/4.0/support/fuzzer/keymaster4_utils_fuzzer.cpp
@@ -46,33 +46,42 @@
support::getOsVersion();
support::getOsPatchlevel();
- VerificationToken token;
- token.challenge = mFdp->ConsumeIntegral<uint64_t>();
- token.timestamp = mFdp->ConsumeIntegral<uint64_t>();
- token.securityLevel = mFdp->PickValueInArray(kSecurityLevel);
- size_t vectorSize = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxVectorSize);
- token.mac.resize(vectorSize);
- for (size_t n = 0; n < vectorSize; ++n) {
- token.mac[n] = n;
+ while (mFdp->remaining_bytes() > 0) {
+ auto keymaster_function = mFdp->PickValueInArray<const std::function<void()>>({
+ [&]() {
+ VerificationToken token;
+ token.challenge = mFdp->ConsumeIntegral<uint64_t>();
+ token.timestamp = mFdp->ConsumeIntegral<uint64_t>();
+ token.securityLevel = mFdp->PickValueInArray(kSecurityLevel);
+ size_t vectorSize = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxVectorSize);
+ token.mac.resize(vectorSize);
+ for (size_t n = 0; n < vectorSize; ++n) {
+ token.mac[n] = mFdp->ConsumeIntegral<uint8_t>();
+ }
+ std::optional<std::vector<uint8_t>> serialized =
+ serializeVerificationToken(token);
+ if (serialized.has_value()) {
+ std::optional<VerificationToken> deserialized =
+ deserializeVerificationToken(serialized.value());
+ }
+ },
+ [&]() {
+ std::vector<uint8_t> dataVector;
+ size_t size = mFdp->ConsumeIntegralInRange<size_t>(0, sizeof(hw_auth_token_t));
+ dataVector = mFdp->ConsumeBytes<uint8_t>(size);
+ support::blob2hidlVec(dataVector.data(), dataVector.size());
+ support::blob2hidlVec(dataVector);
+ HardwareAuthToken authToken = support::hidlVec2AuthToken(dataVector);
+ hidl_vec<uint8_t> volatile hidlVector = support::authToken2HidlVec(authToken);
+ },
+ [&]() {
+ std::string str = mFdp->ConsumeRandomLengthString(kMaxCharacters);
+ support::blob2hidlVec(str);
+ },
+ });
+ keymaster_function();
}
- std::optional<std::vector<uint8_t>> serialized = serializeVerificationToken(token);
- if (serialized.has_value()) {
- std::optional<VerificationToken> deserialized =
- deserializeVerificationToken(serialized.value());
- }
-
- std::vector<uint8_t> dataVector;
- size_t size = mFdp->ConsumeIntegralInRange<size_t>(0, sizeof(hw_auth_token_t));
- dataVector = mFdp->ConsumeBytes<uint8_t>(size);
- support::blob2hidlVec(dataVector.data(), dataVector.size());
-
- support::blob2hidlVec(dataVector);
-
- std::string str = mFdp->ConsumeRandomLengthString(kMaxCharacters);
- support::blob2hidlVec(str);
-
- HardwareAuthToken authToken = support::hidlVec2AuthToken(dataVector);
- hidl_vec<uint8_t> volatile hidlVector = support::authToken2HidlVec(authToken);
+ return;
}
void KeyMaster4UtilsFuzzer::process(const uint8_t* data, size_t size) {
diff --git a/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IComponent.aidl b/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IComponent.aidl
index c7d8a97..7d58340 100644
--- a/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IComponent.aidl
+++ b/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IComponent.aidl
@@ -49,8 +49,12 @@
long blockPoolId;
android.hardware.media.c2.IConfigurable configurable;
}
+ parcelable C2AidlGbAllocator {
+ android.hardware.media.c2.IGraphicBufferAllocator igba;
+ ParcelFileDescriptor waitableFd;
+ }
union BlockPoolAllocator {
int allocatorId;
- android.hardware.media.c2.IGraphicBufferAllocator igba;
+ android.hardware.media.c2.IComponent.C2AidlGbAllocator allocator;
}
}
diff --git a/media/c2/aidl/android/hardware/media/c2/IComponent.aidl b/media/c2/aidl/android/hardware/media/c2/IComponent.aidl
index a7d94b1..fc923ab 100644
--- a/media/c2/aidl/android/hardware/media/c2/IComponent.aidl
+++ b/media/c2/aidl/android/hardware/media/c2/IComponent.aidl
@@ -22,6 +22,8 @@
import android.hardware.media.c2.IConfigurable;
import android.hardware.media.c2.IGraphicBufferAllocator;
import android.hardware.media.c2.WorkBundle;
+import android.os.ParcelFileDescriptor;
+
/**
* Interface for an AIDL Codec2 component.
@@ -46,6 +48,18 @@
}
/**
+ * C2AIDL allocator interface along with a waitable fd.
+ *
+ * The interface is used from a specific type of C2BlockPool to allocate
+ * graphic blocks. the waitable fd is used to create a specific type of
+ * C2Fence which can be used for waiting until to allocate is not blocked.
+ */
+ parcelable C2AidlGbAllocator {
+ IGraphicBufferAllocator igba;
+ ParcelFileDescriptor waitableFd;
+ }
+
+ /**
* Allocator for C2BlockPool.
*
* C2BlockPool will use a C2Allocator which is specified by an id.
@@ -53,7 +67,7 @@
*/
union BlockPoolAllocator {
int allocatorId;
- IGraphicBufferAllocator igba;
+ C2AidlGbAllocator allocator;
}
/**
diff --git a/rebootescrow/aidl/default/Android.bp b/rebootescrow/aidl/default/Android.bp
index 4409314..7f9b6d6 100644
--- a/rebootescrow/aidl/default/Android.bp
+++ b/rebootescrow/aidl/default/Android.bp
@@ -42,10 +42,10 @@
cc_binary {
name: "android.hardware.rebootescrow-service.default",
- init_rc: ["rebootescrow-default.rc"],
relative_install_path: "hw",
- vintf_fragments: ["rebootescrow-default.xml"],
vendor: true,
+ installable: false, // installed in APEX
+
srcs: [
"service.cpp",
],
@@ -53,12 +53,14 @@
"-Wall",
"-Werror",
],
+ stl: "c++_static",
shared_libs: [
- "libbase",
"libbinder_ndk",
- "android.hardware.rebootescrow-V1-ndk",
+ "liblog",
],
static_libs: [
+ "android.hardware.rebootescrow-V1-ndk",
+ "libbase",
"libhadamardutils",
"librebootescrowdefaultimpl",
],
@@ -97,3 +99,35 @@
],
test_suites: ["device-tests"],
}
+
+prebuilt_etc {
+ name: "rebootescrow-default.rc",
+ src: "rebootescrow-default.rc",
+ installable: false,
+}
+
+prebuilt_etc {
+ name: "rebootescrow-default.xml",
+ src: "rebootescrow-default.xml",
+ sub_dir: "vintf",
+ installable: false,
+}
+
+apex {
+ name: "com.android.hardware.rebootescrow",
+ manifest: "apex_manifest.json",
+ file_contexts: "apex_file_contexts",
+ key: "com.android.hardware.key",
+ certificate: ":com.android.hardware.certificate",
+ vendor: true,
+ updatable: false,
+
+ binaries: [
+ "android.hardware.rebootescrow-service.default",
+ ],
+ prebuilts: [
+ "rebootescrow-default.rc",
+ "rebootescrow-default.xml",
+ "android.hardware.reboot_escrow.prebuilt.xml", // <feature>
+ ],
+}
diff --git a/rebootescrow/aidl/default/apex_file_contexts b/rebootescrow/aidl/default/apex_file_contexts
new file mode 100644
index 0000000..aa84984
--- /dev/null
+++ b/rebootescrow/aidl/default/apex_file_contexts
@@ -0,0 +1,3 @@
+(/.*)? u:object_r:vendor_file:s0
+/etc(/.*)? u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.rebootescrow-service\.default u:object_r:hal_rebootescrow_default_exec:s0
diff --git a/rebootescrow/aidl/default/apex_manifest.json b/rebootescrow/aidl/default/apex_manifest.json
new file mode 100644
index 0000000..8be495b
--- /dev/null
+++ b/rebootescrow/aidl/default/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.hardware.rebootescrow",
+ "version": 1
+}
\ No newline at end of file
diff --git a/rebootescrow/aidl/default/rebootescrow-default.rc b/rebootescrow/aidl/default/rebootescrow-default.rc
index ad90465..024dd6d 100644
--- a/rebootescrow/aidl/default/rebootescrow-default.rc
+++ b/rebootescrow/aidl/default/rebootescrow-default.rc
@@ -1,4 +1,4 @@
-service vendor.rebootescrow-default /vendor/bin/hw/android.hardware.rebootescrow-service.default
+service vendor.rebootescrow-default /apex/com.android.hardware.rebootescrow/bin/hw/android.hardware.rebootescrow-service.default
interface aidl android.hardware.rebootescrow.IRebootEscrow/default
class hal
user system
diff --git a/security/authgraph/aidl/Android.bp b/security/authgraph/aidl/Android.bp
new file mode 100644
index 0000000..d94f640
--- /dev/null
+++ b/security/authgraph/aidl/Android.bp
@@ -0,0 +1,88 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.security.authgraph",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/security/authgraph/*.aidl",
+ ],
+ stability: "vintf",
+ frozen: false,
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ apps_enabled: false,
+ },
+ rust: {
+ enabled: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
+ },
+ },
+}
+
+// cc_defaults that includes the latest Authgraph AIDL library.
+// Modules that depend on Authgraph directly can include this cc_defaults to avoid
+// managing dependency versions explicitly.
+cc_defaults {
+ name: "authgraph_use_latest_hal_aidl_ndk_static",
+ static_libs: [
+ "android.hardware.security.authgraph-V1-ndk",
+ ],
+}
+
+cc_defaults {
+ name: "authgraph_use_latest_hal_aidl_ndk_shared",
+ shared_libs: [
+ "android.hardware.security.authgraph-V1-ndk",
+ ],
+}
+
+cc_defaults {
+ name: "authgraph_use_latest_hal_aidl_cpp_static",
+ static_libs: [
+ "android.hardware.security.authgraph-V1-cpp",
+ ],
+}
+
+cc_defaults {
+ name: "authgraph_use_latest_hal_aidl_cpp_shared",
+ shared_libs: [
+ "android.hardware.security.authgraph-V1-cpp",
+ ],
+}
+
+// A rust_defaults that includes the latest Authgraph AIDL library.
+// Modules that depend on Authgraph directly can include this rust_defaults to avoid
+// managing dependency versions explicitly.
+rust_defaults {
+ name: "authgraph_use_latest_hal_aidl_rust",
+ rustlibs: [
+ "android.hardware.security.authgraph-V1-rust",
+ ],
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Arc.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Arc.aidl
new file mode 100644
index 0000000..dc86fbd
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Arc.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable Arc {
+ byte[] arc;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Error.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Error.aidl
new file mode 100644
index 0000000..1a78b54
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Error.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum Error {
+ OK = 0,
+ INVALID_PEER_NONCE = (-1) /* -1 */,
+ INVALID_PEER_KE_KEY = (-2) /* -2 */,
+ INVALID_IDENTITY = (-3) /* -3 */,
+ INVALID_CERT_CHAIN = (-4) /* -4 */,
+ INVALID_SIGNATURE = (-5) /* -5 */,
+ INVALID_KE_KEY = (-6) /* -6 */,
+ INVALID_PUB_KEY_IN_KEY = (-7) /* -7 */,
+ INVALID_PRIV_KEY_ARC_IN_KEY = (-8) /* -8 */,
+ INVALID_SHARED_KEY_ARCS = (-9) /* -9 */,
+ MEMORY_ALLOCATION_FAILED = (-10) /* -10 */,
+ INCOMPATIBLE_PROTOCOL_VERSION = (-11) /* -11 */,
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
new file mode 100644
index 0000000..2c56f33
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+/* @hide */
+@VintfStability
+interface IAuthGraphKeyExchange {
+ android.hardware.security.authgraph.SessionInitiationInfo create();
+ android.hardware.security.authgraph.KeInitResult init(in android.hardware.security.authgraph.PubKey peerPubKey, in android.hardware.security.authgraph.Identity peerId, in byte[] peerNonce, in int peerVersion);
+ android.hardware.security.authgraph.SessionInfo finish(in android.hardware.security.authgraph.PubKey peerPubKey, in android.hardware.security.authgraph.Identity peerId, in android.hardware.security.authgraph.SessionIdSignature peerSignature, in byte[] peerNonce, in int peerVersion, in android.hardware.security.authgraph.Key ownKey);
+ android.hardware.security.authgraph.Arc[2] authenticationComplete(in android.hardware.security.authgraph.SessionIdSignature peerSignature, in android.hardware.security.authgraph.Arc[2] sharedKeys);
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Identity.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Identity.aidl
new file mode 100644
index 0000000..bd5453e
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Identity.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable Identity {
+ byte[] identity;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/KeInitResult.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/KeInitResult.aidl
new file mode 100644
index 0000000..8c91523
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/KeInitResult.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable KeInitResult {
+ android.hardware.security.authgraph.SessionInitiationInfo sessionInitiationInfo;
+ android.hardware.security.authgraph.SessionInfo sessionInfo;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Key.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Key.aidl
new file mode 100644
index 0000000..5b4ebbf
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Key.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable Key {
+ @nullable android.hardware.security.authgraph.PubKey pubKey;
+ @nullable android.hardware.security.authgraph.Arc arcFromPBK;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PlainPubKey.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PlainPubKey.aidl
new file mode 100644
index 0000000..f070bfa
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PlainPubKey.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable PlainPubKey {
+ byte[] plainPubKey;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PubKey.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PubKey.aidl
new file mode 100644
index 0000000..4c3376e
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PubKey.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+union PubKey {
+ android.hardware.security.authgraph.PlainPubKey plainKey;
+ android.hardware.security.authgraph.SignedPubKey signedKey;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionIdSignature.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionIdSignature.aidl
new file mode 100644
index 0000000..6dabc0a
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionIdSignature.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable SessionIdSignature {
+ byte[] signature;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInfo.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInfo.aidl
new file mode 100644
index 0000000..427962b
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInfo.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable SessionInfo {
+ android.hardware.security.authgraph.Arc[2] sharedKeys;
+ byte[] sessionId;
+ android.hardware.security.authgraph.SessionIdSignature signature;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInitiationInfo.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInitiationInfo.aidl
new file mode 100644
index 0000000..bf55e74
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInitiationInfo.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable SessionInitiationInfo {
+ android.hardware.security.authgraph.Key key;
+ android.hardware.security.authgraph.Identity identity;
+ byte[] nonce;
+ int version;
+}
diff --git a/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SignedPubKey.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SignedPubKey.aidl
new file mode 100644
index 0000000..3dbaed8
--- /dev/null
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SignedPubKey.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable SignedPubKey {
+ byte[] signedPubKey;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Arc.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.aidl
new file mode 100644
index 0000000..855ce5c
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * This is the definition of the data format of an Arc.
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable Arc {
+ /**
+ * The messages exchanged between the domains in the AuthGraph protocol are called Arcs.
+ * An arc is simply AES-GCM. Encryption of a payload P with a key K and additional
+ * authentication data (AAD) D: (i.e. Arc = Enc(K, P, D)).
+ *
+ * Data is CBOR-encoded according to the `Arc` CDDL definition in Arc.cddl.
+ */
+ byte[] arc;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl
new file mode 100644
index 0000000..4c1b965
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl
@@ -0,0 +1,115 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+Arc = [ ; COSE_Encrypt0 [RFC9052 s5.2]
+ protected : bstr .cbor ArcProtectedHeaders,
+ unprotected : {
+ 5 : bstr .size 12 ; IV
+ },
+ ciphertext : bstr ; Enc(K, bstr .cbor Payload, encoded ArcEncStruct)
+]
+
+ArcProtectedHeaders = {
+ 1 : 3, ; Algorithm: AES-GCM mode w/ 256-bit key, 128-bit tag
+ ? -70001 : { + Permission }, ; One or more Permissions
+ ? -70002 : { + Limitation }, ; One or more Limitations
+ ? -70003 : int, ; Timestamp in milliseconds since some starting point (generally
+ ; the most recent device boot) which all of the applications within
+ ; the secure domain must agree upon
+ ? -70004 : bstr .size 16, ; Nonce used in key exchange methods
+ ? -70005 : PayloadType, ; Payload type, if needed to disambiguate, when processing an arc
+ ? -70006 : int, ; Version of the payload structure (if applicable)
+ ? -70007 : int, ; Sequence number (if needed to prevent replay attacks)
+ ? -70008 : Direction ; Direction of the encryption key (i.e. whether it is used to
+ ; encrypt incoming messages or outgoing messages)
+ ? -70009 : bool, ; "authentication_completed" - this is used during authenticated
+ ; key exchange indicate whether signature verification is done
+ ? -70010 : bstr .size 32 ; "session_id" computed during key exchange protocol
+}
+
+; Permissions indicate what an arc can be used with. Permissions are added to an arc during the
+; `create()` primitive operation and are propagated during `mint` and `snap` primitive operations.
+Permission = &(
+ -4770552 : IdentityEncoded, ; "source_id" - in the operations performed by a source, the
+ ; source adds its own identity to the permissions of an arc.
+ -4770553 : IdentityEncoded, ; "sink_id" - in the operations performed by a sink, the sink
+ ; adds its own identity to the permissions of an arc.
+ -4770555 : [ +IdentityEncoded ] ; "minting_allowed" - defines the set of TA identities
+ ; to whom the payload key is allowed to be minted.
+ -4770556 : bool ; "deleted_on_biometric_change" - A Boolean value that
+ ; indicates whether an auth key issued from a biometric TA is
+ ; invalidated on new biometric enrollment or removal of all
+ ; biometrics.
+)
+
+; Limitations indicate what restrictions are applied on the usage of an arc. Permissions are added
+; to an arc during the `create` primitive operation and are propagated during `snap` primitive
+; operation.
+Limitation = &(
+ -4770554 : bstr, ; "challenge" - is added to an arc that transfers an auth key to a channel
+ ; key, in order to ensure the freshness of the authentication.
+ ; A challenge is issued by a sink (e.g. Keymint TA, Biometric TAs).
+)
+
+; INCLUDE Identity.cddl for: Identity
+IdentityEncoded = bstr .cbor Identity
+
+Direction = &(
+ In: 1,
+ Out: 2,
+)
+
+PayloadType = &(
+ SecretKey: 1,
+ Arc: 2,
+ ; Any other payload types should also be defined here
+)
+
+Payload = &(
+ SecretKey,
+ Arc,
+ ; Any other payload formats should also be defined here
+)
+
+SecretKey = &( ; One of the payload types of an Arc is a secret key
+ SymmetricKey,
+ ECPrivateKey, ; Private key of a key pair generated for key exchange
+)
+
+ECPrivateKey = { ; COSE_Key [RFC9052 s7]
+ 1 : 2, ; Key type : EC2
+ 3 : -25, ; Algorithm: ECDH ES w/ HKDF 256 - generate key directly
+ ? 4 : [7], ; Key_ops: [derive key]
+ -1 : 1, ; Curve: P-256
+ ? -2 : bstr, ; x coordinate
+ ? -3 : bstr, ; y coordinate
+ -4 : bstr, ; private key (d)
+}
+
+SymmetricKey = { ; COSE_Key [RFC9052 s7] - For symmetric key encryption
+ 1 : 4, ; Key type : Symmetric
+ 3 : 3, ; Algorithm : AES-GCM mode w/ 256-bit key, 128-bit tag
+ 4 : [ 4 ], ; Key_ops: [decrypt]
+ -1 : bstr .size 32, ; Key value (k)
+}
+
+ArcEncStruct = [ ; COSE_Enc_structure [RFC9052 s5.3]
+ context : "Encrypt0",
+ protected : bstr .cbor ArcProtectedHeaders,
+ external_aad : bstr .size 0,
+]
+
+; INCLUDE generateCertificateRequestV2.cddl for: PubKeyEd25519, PubKeyECDSA256, PubKeyECDSA384
+; from hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/DicePolicy.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/DicePolicy.cddl
new file mode 100644
index 0000000..a7dcbc6
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/DicePolicy.cddl
@@ -0,0 +1,33 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+DicePolicy = [
+ 1, ; dice policy version
+ + nodeConstraintList ; for each entry in dice chain
+]
+
+nodeConstraintList = [
+ * nodeConstraint
+]
+
+; We may add a hashConstraint item later
+nodeConstraint = exactMatchConstraint / geConstraint
+
+exactMatchConstraint = [1, keySpec, value]
+geConstraint = [2, keySpec, int]
+
+keySpec = [value+]
+
+value = bool / int / tstr / bstr
\ No newline at end of file
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Error.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/Error.aidl
new file mode 100644
index 0000000..1ad6054
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Error.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * AuthGraph error codes. Aidl will return these error codes as service specific errors in
+ * EX_SERVICE_SPECIFIC.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum Error {
+ /* Success */
+ OK = 0,
+ /* Invalid peer nonce for key exchange */
+ INVALID_PEER_NONCE = -1,
+ /* Invalid key exchange public key by the peer */
+ INVALID_PEER_KE_KEY = -2,
+ /* Invalid identity of the peer */
+ INVALID_IDENTITY = -3,
+ /* Invalid certificate chain in the identity of the peer */
+ INVALID_CERT_CHAIN = -4,
+ /* Invalid signature by the peer */
+ INVALID_SIGNATURE = -5,
+ /* Invalid key exchange key created by a particular party themselves to be used as a handle */
+ INVALID_KE_KEY = -6,
+ /* Invalid public key in the `Key` struct */
+ INVALID_PUB_KEY_IN_KEY = -7,
+ /* Invalid private key arc in the `Key` struct */
+ INVALID_PRIV_KEY_ARC_IN_KEY = -8,
+ /* Invalid shared key arcs */
+ INVALID_SHARED_KEY_ARCS = -9,
+ /* Memory allocation failed */
+ MEMORY_ALLOCATION_FAILED = -10,
+ /* The protocol version negotiated with the sink is incompatible */
+ INCOMPATIBLE_PROTOCOL_VERSION = -11,
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/ExplicitKeyDiceCertChain.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/ExplicitKeyDiceCertChain.cddl
new file mode 100644
index 0000000..3de5617
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/ExplicitKeyDiceCertChain.cddl
@@ -0,0 +1,30 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+ExplicitKeyDiceCertChain = [
+ 1, ; version, hopefully will never change
+ DiceCertChainInitialPayload,
+ * DiceChainEntry
+]
+
+DiceCertChainInitialPayload = {
+ -4670552 : bstr .cbor PubKeyEd25519 /
+ bstr .cbor PubKeyECDSA256 /
+ bstr .cbor PubKeyECDSA384 ; subjectPublicKey
+}
+
+; INCLUDE generateCertificateRequestV2.cddl for: PubKeyEd25519, PubKeyECDSA256, PubKeyECDSA384,
+; DiceChainEntry
+; from hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
new file mode 100644
index 0000000..6ceb09c
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.Arc;
+import android.hardware.security.authgraph.Identity;
+import android.hardware.security.authgraph.KeInitResult;
+import android.hardware.security.authgraph.Key;
+import android.hardware.security.authgraph.PubKey;
+import android.hardware.security.authgraph.SessionIdSignature;
+import android.hardware.security.authgraph.SessionInfo;
+import android.hardware.security.authgraph.SessionInitiationInfo;
+
+/**
+ * AuthGraph interface definition for authenticated key exchange between two parties: P1 (source)
+ * and P2 (sink).
+ * Pre-requisites: each participant should have a:
+ * 1. Persistent identity - e.g. a signing key pair with a self signed certificate or a DICE
+ * certificate chain.
+ * 2. A symmetric encryption key kept in memory with per-boot life time of the participant
+ * (a.k.a per-boot key)
+ *
+ * ErrorCodes are defined in android.hardware.security.authgraph.ErrorCode.aidl.
+ * @hide
+ */
+@VintfStability
+interface IAuthGraphKeyExchange {
+ /**
+ * This method is invoked on P1 (source).
+ * Create an ephermeral EC key pair on NIST curve P-256 and a nonce (of 16 bytes) for
+ * key exchange.
+ *
+ * @return SessionInitiationInfo including the `Key` containing the public key of the created
+ * key pair and an arc from the per-boot key to the private key, the nonce, the persistent
+ * identity and the latest protocol version (i.e. AIDL version) supported.
+ *
+ * Note: The arc from the per-boot key to the private key in `Key` of the return type:
+ * `SessionInitiationInfo` serves two purposes:
+ * i. A mapping to correlate `create` and `finish` calls to P1 in a particular instance of the
+ * key exchange protocol.
+ * ii.A way to minimize the in-memory storage (P1 can include the nonce in the protected headers
+ * of the arc).
+ * However, P1 should maintain some form of in-memory record to be able to verify that the input
+ * `Key` sent to `finish` is from an unfinished instance of a key exchange protocol, to prevent
+ * any replay attacks in `finish`.
+ */
+ SessionInitiationInfo create();
+
+ /**
+ * This method is invoked on P2 (sink).
+ * Perform the following steps for key exchange:
+ * 0. If either `peerPubKey`, `peerId`, `peerNonce` is not in the expected format, return
+ * errors: INVALID_PEER_KE_KEY, INVALID_IDENTITY, INVALID_PEER_NONCE respectively.
+ * 1. Create an ephemeral EC key pair on NIST curve P-256.
+ * 2. Create a nonce (of 16 bytes).
+ * 3. Compute the diffie-hellman shared secret: Z.
+ * 4. Compute a salt = bstr .cbor [
+ * source_version: int, ; from input `peerVersion`
+ * sink_pub_key: bstr .cbor PlainPubKey, ; from step #1
+ * source_pub_key: bstr .cbor PlainPubKey, ; from input `peerPubKey`
+ * sink_nonce: bstr .size 16, ; from step #2
+ * source_nonce: bstr .size 16, ; from input `peerNonce`
+ * sink_cert_chain: bstr .cbor ExplicitKeyDiceCertChain, ; from own identity
+ * source_cert_chain: bstr .cbor ExplicitKeyDiceCertChain, ; from input `peerId`
+ * ]
+ * 5. Extract a cryptographic secret S from Z, using the salt from #4 above.
+ * 6. Derive two symmetric encryption keys of 256 bits with:
+ * i. b"KE_ENCRYPTION_KEY_SOURCE_TO_SINK" as context for the key used to encrypt incoming
+ * messages
+ * ii. b"KE_ENCRYPTION_KEY_SINK_TO_SOURCE" as context for the key used to encrypt outgoing
+ * messages
+ * 7. Create arcs from the per-boot key to each of the two shared keys from step #6 and
+ * mark authentication_complete = false in arcs' protected headers.
+ * 8. Derive a MAC key with b"KE_HMAC_KEY" as the context.
+ * 9. Compute session_id_input = bstr .cbor [
+ * sink_nonce: bstr .size 16,
+ * source_nonce: bstr .size 16,
+ * ],
+ * 10.Compute a session_id as a 256 bits HMAC over the session_id_input from step#9 with
+ * the key from step #8.
+ * 11.Create a signature over the session_id from step #10, using the signing key which is
+ * part of the party's identity.
+ *
+ * @param peerPubKey - the public key of the key pair created by the peer (P1) for key exchange
+ *
+ * @param peerId - the persistent identity of the peer
+ *
+ * @param peerNonce - nonce created by the peer
+ *
+ * @param peerVersion - an integer representing the latest protocol version (i.e. AIDL version)
+ * supported by the peer
+ *
+ * @return KeInitResult including the `Key` containing the public key of the created key pair,
+ * the nonce, the persistent identity, two shared key arcs from step #7, session id, signature
+ * over the session id and the negotiated protocol version. The negotiated protocol version
+ * should be less than or equal to the peer's version.
+ *
+ * Note: The two shared key arcs in the return type: `KeInitResult` serves two purposes:
+ * i. A mapping to correlate `init` and `authenticationComplete` calls to P2 in a particular
+ * instance of the key exchange protocol.
+ * ii.A way to minimize the in-memory storage of P2 allocated for key exchange.
+ * However, P2 should maintain some in-memory record to be able to verify that the input
+ * `sharedkeys` sent to `authenticationComplete` and to any subsequent AuthGraph protocol
+ * methods are valid shared keys agreed with the party identified by `peerId`, to prevent
+ * any replay attacks in `authenticationComplete` and in any subsequent AuthGraph protocol
+ * methods which use the shared keys to encrypt the secret messages.
+ */
+ KeInitResult init(
+ in PubKey peerPubKey, in Identity peerId, in byte[] peerNonce, in int peerVersion);
+
+ /**
+ * This method is invoked on P1 (source).
+ * Perform the following steps:
+ * 0. If either `peerPubKey`, `peerId`, `peerNonce` is not in the expected format, return
+ * errors: INVALID_PEER_KE_KEY, INVALID_IDENTITY, INVALID_PEER_NONCE respectively. If
+ * `peerVersion` is greater than the version advertised in `create`, return error:
+ * INCOMPATIBLE_PROTOCOL_VERSION.
+ * If `ownKey` is not in the in-memory records for unfinished instances of a key
+ * exchange protocol, return error: INVALID_KE_KEY. Similarly, if the public key or the
+ * arc containing the private key in `ownKey` is invalid, return INVALID_PUB_KEY_IN_KEY
+ * and INVALID_PRIV_KEY_ARC_IN_KEY respectively.
+ * 1. Compute the diffie-hellman shared secret: Z.
+ * 2. Compute a salt = bstr .cbor [
+ * source_version: int, ; the protocol version used in `create`
+ * sink_pub_key: bstr .cbor PlainPubKey, ; from input `peerPubKey`
+ * source_pub_key: bstr .cbor PlainPubKey, ; from the output of `create`
+ * sink_nonce: bstr .size 16, ; from input `peerNonce`
+ * source_nonce: bstr .size 16, ; from the output of `create`
+ * sink_cert_chain: bstr .cbor ExplicitKeyDiceCertChain, ; from input `peerId`
+ * source_cert_chain: bstr .cbor ExplicitKeyDiceCertChain, ; from own identity
+ * ]
+ * 3. Extract a cryptographic secret S from Z, using the salt from #2 above.
+ * 4. Derive two symmetric encryption keys of 256 bits with:
+ * i. b"KE_ENCRYPTION_KEY_SOURCE_TO_SINK" as context for the key used to encrypt outgoing
+ * messages
+ * ii. b"KE_ENCRYPTION_KEY_SINK_TO_SOURCE" as context for the key used to encrypt incoming
+ * messages
+ * 5. Derive a MAC key with b"KE_HMAC_KEY" as the context.
+ * 6. Compute session_id_input = bstr .cbor [
+ * sink_nonce: bstr .size 16,
+ * source_nonce: bstr .size 16,
+ * ],
+ * 7. Compute a session_id as a 256 bits HMAC over the session_id_input from step #6 with
+ * the key from step #5.
+ * 8. Verify the peer's signature over the session_id from step #7. If successful, proceed,
+ * otherwise, return error: INVALID_SIGNATURE.
+ * 9. Create arcs from the per-boot key to each of the two shared keys from step #4 and
+ * mark authentication_complete = true in arcs' protected headers.
+ * 10.Create a signature over the session_id from step #7, using the signing key which is
+ * part of the party's identity.
+ *
+ * @param peerPubKey - the public key of the key pair created by the peer (P2) for key exchange
+ *
+ * @param peerId - the persistent identity of the peer
+ *
+ * @param peerSignature - the signature created by the peer over the session id computed by the
+ * peer
+ *
+ * @param peerNonce - nonce created by the peer
+ *
+ * @param peerVersion - an integer representing the protocol version (i.e. AIDL version)
+ * negotiated with the peer
+ *
+ * @param ownKey - the key created by P1 (source) in `create()` for key exchange
+ *
+ * @return SessionInfo including the two shared key arcs from step #9, session id and the
+ * signature over the session id.
+ *
+ * Note: The two shared key arcs in the return type: `SessionInfo` serves two purposes:
+ * i. A mapping to correlate the key exchange protocol taken place with a particular peer and
+ * subsequent AuthGraph protocols execued with the same peer.
+ * ii.A way to minimize the in-memory storage for shared keys.
+ * However, P1 should maintain some in-memory record to be able to verify that the shared key
+ * arcs sent to any subsequent AuthGraph protocol methods are valid shared keys agreed with the
+ * party identified by `peerId`, to prevent any replay attacks.
+ */
+ SessionInfo finish(in PubKey peerPubKey, in Identity peerId,
+ in SessionIdSignature peerSignature, in byte[] peerNonce, in int peerVersion,
+ in Key ownKey);
+
+ /**
+ * This method is invoked on P2 (sink).
+ * Perform the following steps:
+ * 0. If input `sharedKeys` is invalid (i.e. they cannot be decrypted with P2's per-boot key
+ * or they are not in P2's in-memory records as valid shared keys agreed with the party
+ * identified by `peerId`), return error: INVALID_SHARED_KEY_ARCS.
+ * 1. Verify that both shared key arcs have the same session id and peer identity.
+ * 2. Verify the peer's signature over the session id attached to the shared key arcs'
+ * headers. If successful, proceed, otherwise, return error: INVALID_SIGNATURE.
+ * 3. Mark authentication_complete = true in the shared key arcs' headers
+ *
+ * @param peerSignature - the signature created by the peer over the session id computed by the
+ * peer
+ *
+ * @param sharedKeys - two shared key arcs created by P2 in `init`. P2 obtains from the arcs'
+ * protected headers, the session id and the peer's identity to verify the
+ * peer's signature over the session id.
+ *
+ * @return Arc[] - an array of two updated shared key arcs
+ */
+ Arc[2] authenticationComplete(in SessionIdSignature peerSignature, in Arc[2] sharedKeys);
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Identity.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/Identity.aidl
new file mode 100644
index 0000000..9e350e8
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Identity.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * Persistent (versioned) identity of a participant of Authgraph key exchange.
+ * Identity consists of two main parts:
+ * 1. a certificate chain (e.g. a DICE certificate chain)
+ * 2. (optional) a policy specifying how to verify the certificate chain - if a policy is not
+ * provided, a simple byte-to-byte comparison of the certificate chain is assumed.
+ *
+ * During identity verification, the certificate chain of the identity attached to the access
+ * request is compared against the policy of the identity attached to the persistent resources.
+ *
+ * The usage of policy based identity verification in Authgraph is three-fold:
+ * 1. Retain access to persistent resources for the newer versions of the party who
+ * created them, even when parts of the certificate chain are updated in the new version.
+ * 2. Deny access to the new persistent resources for the older versions of the party
+ * who created the new persistent resources.
+ * 3. Trigger rotation of critical keys encrypted in persistent arcs created by the previous
+ * version of the party, by including an updated policy in the identity attached to the
+ * access request.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable Identity {
+ /* Data is CBOR-encoded according to the `Identity` CDDL definition in Identity.cddl */
+ byte[] identity;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Identity.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/Identity.cddl
new file mode 100644
index 0000000..0419421
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Identity.cddl
@@ -0,0 +1,23 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+Identity = [
+ 1, ; Version
+ cert_chain: bstr .cbor ExplicitKeyDiceCertChain,
+ policy: bstr .cbor DicePolicy / nil,
+]
+
+; INCLUDE ExplicitKeyDiceCertChain.cddl for: ExplicitKeyDiceCertChain
+; INCLUDE DicePolicy.cddl for: DicePolicy
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/KeInitResult.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/KeInitResult.aidl
new file mode 100644
index 0000000..b4ae451
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/KeInitResult.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.SessionInfo;
+import android.hardware.security.authgraph.SessionInitiationInfo;
+
+/**
+ * The return type for the init() step of authenticated key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable KeInitResult {
+ /**
+ * Session initiation information.
+ */
+ SessionInitiationInfo sessionInitiationInfo;
+
+ /**
+ * Session information.
+ */
+ SessionInfo sessionInfo;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Key.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/Key.aidl
new file mode 100644
index 0000000..11fe174
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Key.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.Arc;
+import android.hardware.security.authgraph.PubKey;
+
+/**
+ * The type that encapsulates a key. Key can be either a symmetric key or an asymmetric key.
+ * If it is an asymmetric key, it is used for key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable Key {
+ /**
+ * If the Key is an asymmetric key, public key should be present.
+ */
+ @nullable PubKey pubKey;
+
+ /**
+ * Arc from the per-boot key to the payload key. The payload key is either the symmetric key
+ * or the private key of an asymmetric key, based on the type of the key being created.
+ * This is marked as optional because there are instances where only the public key is returned,
+ * e.g. `init` method in the key exchange protocol.
+ */
+ @nullable Arc arcFromPBK;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.aidl
new file mode 100644
index 0000000..5483ec5
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * One of the two enum variants of the enum type: `PubKey`. This represents the plain public key
+ * material encoded as a COSE_Key.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable PlainPubKey {
+ /* Data is CBOR-encoded according to the `PlainPubKey` CDDL definition in PlainPubKey.cddl */
+ byte[] plainPubKey;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.cddl
new file mode 100644
index 0000000..34b316b
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.cddl
@@ -0,0 +1,24 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+
+; P-256 public key for key exchange.
+PlainPubKey = [ ; COSE_Key [RFC9052 s7]
+ 1 : 2, ; Key type : EC2
+ 3 : -27, ; Algorithm : ECDH-SS + HKDF-256
+ -1 : 1, ; Curve: P256
+ -2 : bstr, ; X coordinate, big-endian
+ -3 : bstr ; Y coordinate, big-endian
+]
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/PubKey.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/PubKey.aidl
new file mode 100644
index 0000000..8640871
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/PubKey.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.PlainPubKey;
+import android.hardware.security.authgraph.SignedPubKey;
+
+/**
+ * The enum type representing the public key of an asymmetric key pair.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+union PubKey {
+ /**
+ * Plain public key material encoded as a COSE_Key.
+ */
+ PlainPubKey plainKey;
+
+ /**
+ * Public key signed with the long term signing key of the party.
+ */
+ SignedPubKey signedKey;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.aidl
new file mode 100644
index 0000000..2fa8b4c
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * Signature computed by a party over the session id during authenticated key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable SessionIdSignature {
+ /* Data is CBOR-encoded according to the `SessionIdSignature` CDDL definition in
+ * SessionIdSignature.cddl */
+ byte[] signature;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.cddl
new file mode 100644
index 0000000..038a0f0
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.cddl
@@ -0,0 +1,33 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+SessionIdSignature = [ ; COSE_Sign1 (untagged) [RFC9052 s4.2]
+ protected: bstr .cbor SessionIdSignatureProtected,
+ unprotected: {},
+ payload: nil, ; session ID payload to be transported separately
+ signature: bstr ; PureEd25519(privateKey, SessionIdSignatureSigStruct) /
+ ; ECDSA(privateKey, SessionIdSignatureSigStruct)
+]
+
+SessionIdSignatureProtected = {
+ 1 : AlgorithmEdDSA / AlgorithmES256,
+}
+
+SessionIdSignatureSigStruct = [ ; Sig_structure for SessionIdSignature [ RFC9052 s4.4]
+ context: "Signature1",
+ protected: bstr SessionIdSignatureProtected,
+ external_aad: bstr .size 0,
+ payload: bstr, ; session ID payload provided separately
+]
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl
new file mode 100644
index 0000000..ef49a1a
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.Arc;
+import android.hardware.security.authgraph.SessionIdSignature;
+
+/**
+ * Session information returned as part of authenticated key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable SessionInfo {
+ /**
+ * The arcs that encrypt the two derived symmetric encryption keys (for two-way communication)
+ * from the party's per-boot key.
+ */
+ Arc[2] sharedKeys;
+
+ /**
+ * The value of the session id computed by the two parties during the authenticate key
+ * exchange. Apart from the usage of the session id by the two peers, session id is also useful
+ * to verify (by a third party) that the key exchange was successful.
+ */
+ byte[] sessionId;
+
+ /**
+ * The signature over the session id, created by the party who computed the session id.
+ *
+ * If there is one or more `DiceChainEntry` in the `ExplicitKeyDiceCertChain` of the party's
+ * identity, the signature is verified with the public key in the leaf of the chain of
+ * DiceChainEntries (i.e the public key in the last of the array of DiceChainEntries).
+ * Otherwise, the signature is verified with the `DiceCertChainInitialPayload`.
+ */
+ SessionIdSignature signature;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl
new file mode 100644
index 0000000..c630d91
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.Arc;
+import android.hardware.security.authgraph.Identity;
+import android.hardware.security.authgraph.Key;
+
+/**
+ * Session initiation information returned as part of authenticated key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable SessionInitiationInfo {
+ /**
+ * An ephemeral EC key created for the ECDH process.
+ */
+ Key key;
+
+ /**
+ * The identity of the party who created the Diffie-Hellman key exchange key.
+ */
+ Identity identity;
+
+ /**
+ * Nonce value specific to this session. The nonce serves three purposes:
+ * 1. freshness of key exchange
+ * 2. creating a session id (a publicly known value related to the exchanged keys)
+ * 3. usage as salt into the HKDF-EXTRACT function during key derivation from the shared DH key
+ */
+ byte[] nonce;
+
+ /**
+ * The protocol version (i.e. AIDL version) - This is used to prevent version downgrade attacks
+ * as follows:
+ * 1. In `create`, the source advertises the latest protocol version supported by the source,
+ * which is given as input to the `init` call on the sink in the input parameter:
+ * `peerVersion`.
+ * 2. In `init`, the sink includes the `peerVersion` in the inputs to the derivation of the
+ * shared keys. Then the sink returns the latest protocol version supported by the sink,
+ * which is given as input to the `finish` call on the source in the input parameter:
+ * `peerVersion`.
+ * 3. In `finish`, the source first checks whether the sink's version is equal or less than the
+ * source's version and includes in the source's version in the inputs to the derivation of
+ * the shared keys.
+ * Analysis: if an attacker-in-the-middle wanted the two parties to use an older (vulnerable)
+ * version of the protocol, they can invoke `init` with a version that is lower than the version
+ * advertised by the source in `create`. However, since both parties include the source's
+ * version in the inputs to the derivation of the shared keys, the two parties won't end up with
+ * the same shared keys in the presence of such an attack. This is detected when checking the
+ * signature on the session id in `finish`, at which point the protocol aborts. Therefore,
+ * an attacker cannot successfully launch a version downgrade attack on this protocol.
+ */
+ int version;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.aidl
new file mode 100644
index 0000000..72ee219
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * One of the two enum variants of the enum type: `PubKey`. This represents the public key signed
+ * with the long term signing key of the party.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable SignedPubKey {
+ /* Data is CBOR-encoded according to the `SignedPubKey` CDDL definition in SignedPubKey.cddl */
+ byte[] signedPubKey;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.cddl
new file mode 100644
index 0000000..f23a492
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.cddl
@@ -0,0 +1,41 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+SignedPubKey = [ ; COSE_Sign1 (untagged) [RFC9052 s4.2]
+ protected: bstr .cbor SignedPubKeyProtected,
+ unprotected: {},
+ payload: bstr .cbor PlainPubKey,
+ signature: bstr ; PureEd25519(privateKey, SignedPubKeySigStruct) /
+ ; ECDSA(privateKey, SignedPubKeySigStruct)
+]
+
+SignedPubKeyProtected = {
+ 1 : AlgorithmEdDSA / AlgorithmES256,
+ ? -70011 : Identity, ; the party who performs the signing operation adds its own
+ ; identity to the protected headers.
+}
+
+SignedPubKeySigStruct = [ ; Sig_structure for SignedPubKey [ RFC9052 s4.4]
+ context: "Signature1",
+ protected: bstr SignedPubKeyProtected,
+ external_aad: bstr .size 0,
+ payload: bstr .cbor PlainPubKey,
+]
+
+AlgorithmES256 = -7 ; [RFC9053 s2.1]
+AlgorithmEdDSA = -8 ; [RFC9053 s2.2]
+
+; INCLUDE PlainPubKey.cddl for: PlainPubKey
+; INCLUDE Identity.cddl for: Identity
\ No newline at end of file
diff --git a/security/authgraph/aidl/vts/functional/Android.bp b/security/authgraph/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..fc13759
--- /dev/null
+++ b/security/authgraph/aidl/vts/functional/Android.bp
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsAidlAuthGraphSessionTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "authgraph_use_latest_hal_aidl_ndk_static",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ srcs: [
+ "AuthGraphSessionTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcrypto",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/security/authgraph/aidl/vts/functional/AuthGraphSessionTest.cpp b/security/authgraph/aidl/vts/functional/AuthGraphSessionTest.cpp
new file mode 100644
index 0000000..d9dea77
--- /dev/null
+++ b/security/authgraph/aidl/vts/functional/AuthGraphSessionTest.cpp
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "authgraph_session_test"
+#include <android-base/logging.h>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/security/authgraph/Error.h>
+#include <aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.h>
+#include <android/binder_manager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+namespace aidl::android::hardware::security::authgraph::test {
+using ::aidl::android::hardware::security::authgraph::Error;
+
+namespace {
+
+// Check that the signature in the encoded COSE_Sign1 data is correct, and that the payload matches.
+// TODO: maybe drop separate payload, and extract it from cose_sign1.payload (and return it).
+void CheckSignature(std::vector<uint8_t>& /*pub_cose_key*/, std::vector<uint8_t>& /*payload*/,
+ std::vector<uint8_t>& /*cose_sign1*/) {
+ // TODO: implement me
+}
+
+void CheckSignature(std::vector<uint8_t>& pub_cose_key, std::vector<uint8_t>& payload,
+ SessionIdSignature& signature) {
+ return CheckSignature(pub_cose_key, payload, signature.signature);
+}
+
+std::vector<uint8_t> SigningKeyFromIdentity(const Identity& identity) {
+ // TODO: This is a CBOR-encoded `Identity` which currently happens to be a COSE_Key with the
+ // pubkey This will change in future.
+ return identity.identity;
+}
+
+} // namespace
+
+class AuthGraphSessionTest : public ::testing::TestWithParam<std::string> {
+ public:
+ enum ErrorType { AIDL_ERROR, BINDER_ERROR };
+
+ union ErrorValue {
+ Error aidl_error;
+ int32_t binder_error;
+ };
+
+ struct ReturnedError {
+ ErrorType err_type;
+ ErrorValue err_val;
+
+ friend bool operator==(const ReturnedError& lhs, const ReturnedError& rhs) {
+ return lhs.err_type == rhs.err_type;
+ switch (lhs.err_type) {
+ case ErrorType::AIDL_ERROR:
+ return lhs.err_val.aidl_error == rhs.err_val.aidl_error;
+ case ErrorType::BINDER_ERROR:
+ return lhs.err_val.binder_error == rhs.err_val.binder_error;
+ }
+ }
+ };
+
+ const ReturnedError OK = {.err_type = ErrorType::AIDL_ERROR, .err_val.aidl_error = Error::OK};
+
+ ReturnedError GetReturnError(const ::ndk::ScopedAStatus& result) {
+ if (result.isOk()) {
+ return OK;
+ }
+ int32_t exception_code = result.getExceptionCode();
+ int32_t error_code = result.getServiceSpecificError();
+ if (exception_code == EX_SERVICE_SPECIFIC && error_code != 0) {
+ ReturnedError re = {.err_type = ErrorType::AIDL_ERROR,
+ .err_val.aidl_error = static_cast<Error>(error_code)};
+ return re;
+ }
+ ReturnedError re = {.err_type = ErrorType::BINDER_ERROR,
+ .err_val.binder_error = exception_code};
+ return re;
+ }
+
+ // Build the parameters for the VTS test by enumerating the available HAL instances
+ static std::vector<std::string> build_params() {
+ auto params = ::android::getAidlHalInstanceNames(IAuthGraphKeyExchange::descriptor);
+ return params;
+ }
+
+ void SetUp() override {
+ ASSERT_TRUE(AServiceManager_isDeclared(GetParam().c_str()))
+ << "No instance declared for " << GetParam();
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ authNode_ = IAuthGraphKeyExchange::fromBinder(binder);
+ ASSERT_NE(authNode_, nullptr) << "Failed to get Binder reference for " << GetParam();
+ }
+
+ void TearDown() override {}
+
+ protected:
+ std::shared_ptr<IAuthGraphKeyExchange> authNode_;
+};
+
+TEST_P(AuthGraphSessionTest, Mainline) {
+ std::shared_ptr<IAuthGraphKeyExchange> source = authNode_;
+ std::shared_ptr<IAuthGraphKeyExchange> sink = authNode_;
+
+ // Step 1: create an ephemeral ECDH key at the source.
+ SessionInitiationInfo source_init_info;
+ ASSERT_EQ(OK, GetReturnError(source->create(&source_init_info)));
+ ASSERT_TRUE(source_init_info.key.pubKey.has_value());
+ ASSERT_TRUE(source_init_info.key.arcFromPBK.has_value());
+
+ // Step 2: pass the source's ECDH public key and other session info to the sink.
+ KeInitResult init_result;
+ ASSERT_EQ(OK, GetReturnError(sink->init(source_init_info.key.pubKey.value(),
+ source_init_info.identity, source_init_info.nonce,
+ source_init_info.version, &init_result)));
+ SessionInitiationInfo sink_init_info = init_result.sessionInitiationInfo;
+ ASSERT_TRUE(sink_init_info.key.pubKey.has_value());
+ // The sink_init_info.arcFromPBK need not be populated, as the ephemeral key agreement
+ // key is no longer needed.
+
+ SessionInfo sink_info = init_result.sessionInfo;
+ ASSERT_EQ((int)sink_info.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+ ASSERT_GT((int)sink_info.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+ std::vector<uint8_t> sink_signing_key = SigningKeyFromIdentity(sink_init_info.identity);
+ CheckSignature(sink_signing_key, sink_info.sessionId, sink_info.signature);
+
+ // Step 3: pass the sink's ECDH public key and other session info to the source, so it can
+ // calculate the same pair of symmetric keys.
+ SessionInfo source_info;
+ ASSERT_EQ(OK, GetReturnError(source->finish(sink_init_info.key.pubKey.value(),
+ sink_init_info.identity, sink_info.signature,
+ sink_init_info.nonce, sink_init_info.version,
+ source_init_info.key, &source_info)));
+ ASSERT_EQ((int)source_info.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+ ASSERT_GT((int)source_info.sessionId.size(), 0) << "Expect non-empty session ID from source";
+ std::vector<uint8_t> source_signing_key = SigningKeyFromIdentity(source_init_info.identity);
+ CheckSignature(source_signing_key, source_info.sessionId, source_info.signature);
+
+ // Both ends should agree on the session ID.
+ ASSERT_EQ(source_info.sessionId, sink_info.sessionId);
+
+ // Step 4: pass the source's session ID info back to the sink, so it can check it and
+ // update the symmetric keys so they're marked as authentication complete.
+ std::array<Arc, 2> auth_complete_result;
+ ASSERT_EQ(OK, GetReturnError(sink->authenticationComplete(
+ source_info.signature, sink_info.sharedKeys, &auth_complete_result)));
+ ASSERT_EQ((int)auth_complete_result.size(), 2)
+ << "Expect two symmetric keys from authComplete()";
+ sink_info.sharedKeys = auth_complete_result;
+
+ // At this point the sink and source have agreed on the same pair of symmetric keys,
+ // encoded as `sink_info.sharedKeys` and `source_info.sharedKeys`.
+}
+
+TEST_P(AuthGraphSessionTest, ParallelSink) {
+ std::shared_ptr<IAuthGraphKeyExchange> source = authNode_;
+ std::shared_ptr<IAuthGraphKeyExchange> sink1 = authNode_;
+ std::shared_ptr<IAuthGraphKeyExchange> sink2 = authNode_;
+
+ // Step 1: create ephemeral ECDH keys at the source.
+ SessionInitiationInfo source_init1_info;
+ ASSERT_EQ(OK, GetReturnError(source->create(&source_init1_info)));
+ ASSERT_TRUE(source_init1_info.key.pubKey.has_value());
+ ASSERT_TRUE(source_init1_info.key.arcFromPBK.has_value());
+ SessionInitiationInfo source_init2_info;
+ ASSERT_EQ(OK, GetReturnError(source->create(&source_init2_info)));
+ ASSERT_TRUE(source_init2_info.key.pubKey.has_value());
+ ASSERT_TRUE(source_init2_info.key.arcFromPBK.has_value());
+
+ // Step 2: pass the source's ECDH public keys and other session info to the sinks.
+ KeInitResult init1_result;
+ ASSERT_EQ(OK, GetReturnError(sink1->init(source_init1_info.key.pubKey.value(),
+ source_init1_info.identity, source_init1_info.nonce,
+ source_init1_info.version, &init1_result)));
+ SessionInitiationInfo sink1_init_info = init1_result.sessionInitiationInfo;
+ ASSERT_TRUE(sink1_init_info.key.pubKey.has_value());
+
+ SessionInfo sink1_info = init1_result.sessionInfo;
+ ASSERT_EQ((int)sink1_info.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+ ASSERT_GT((int)sink1_info.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+ std::vector<uint8_t> sink1_signing_key = SigningKeyFromIdentity(sink1_init_info.identity);
+ CheckSignature(sink1_signing_key, sink1_info.sessionId, sink1_info.signature);
+ KeInitResult init2_result;
+ ASSERT_EQ(OK, GetReturnError(sink2->init(source_init2_info.key.pubKey.value(),
+ source_init2_info.identity, source_init2_info.nonce,
+ source_init2_info.version, &init2_result)));
+ SessionInitiationInfo sink2_init_info = init2_result.sessionInitiationInfo;
+ ASSERT_TRUE(sink2_init_info.key.pubKey.has_value());
+
+ SessionInfo sink2_info = init2_result.sessionInfo;
+ ASSERT_EQ((int)sink2_info.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+ ASSERT_GT((int)sink2_info.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+ std::vector<uint8_t> sink2_signing_key = SigningKeyFromIdentity(sink2_init_info.identity);
+ CheckSignature(sink2_signing_key, sink2_info.sessionId, sink2_info.signature);
+
+ // Step 3: pass each sink's ECDH public key and other session info to the source, so it can
+ // calculate the same pair of symmetric keys.
+ SessionInfo source_info1;
+ ASSERT_EQ(OK, GetReturnError(source->finish(sink1_init_info.key.pubKey.value(),
+ sink1_init_info.identity, sink1_info.signature,
+ sink1_init_info.nonce, sink1_init_info.version,
+ source_init1_info.key, &source_info1)));
+ ASSERT_EQ((int)source_info1.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+ ASSERT_GT((int)source_info1.sessionId.size(), 0) << "Expect non-empty session ID from source";
+ std::vector<uint8_t> source_signing_key1 = SigningKeyFromIdentity(source_init1_info.identity);
+ CheckSignature(source_signing_key1, source_info1.sessionId, source_info1.signature);
+ SessionInfo source_info2;
+ ASSERT_EQ(OK, GetReturnError(source->finish(sink2_init_info.key.pubKey.value(),
+ sink2_init_info.identity, sink2_info.signature,
+ sink2_init_info.nonce, sink2_init_info.version,
+ source_init2_info.key, &source_info2)));
+ ASSERT_EQ((int)source_info2.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+ ASSERT_GT((int)source_info2.sessionId.size(), 0) << "Expect non-empty session ID from source";
+ std::vector<uint8_t> source_signing_key2 = SigningKeyFromIdentity(source_init2_info.identity);
+ CheckSignature(source_signing_key2, source_info2.sessionId, source_info2.signature);
+
+ // Both ends should agree on the session ID.
+ ASSERT_EQ(source_info1.sessionId, sink1_info.sessionId);
+ ASSERT_EQ(source_info2.sessionId, sink2_info.sessionId);
+
+ // Step 4: pass the source's session ID info back to the sink, so it can check it and
+ // update the symmetric keys so they're marked as authentication complete.
+ std::array<Arc, 2> auth_complete_result1;
+ ASSERT_EQ(OK, GetReturnError(sink1->authenticationComplete(
+ source_info1.signature, sink1_info.sharedKeys, &auth_complete_result1)));
+ ASSERT_EQ((int)auth_complete_result1.size(), 2)
+ << "Expect two symmetric keys from authComplete()";
+ sink1_info.sharedKeys = auth_complete_result1;
+ std::array<Arc, 2> auth_complete_result2;
+ ASSERT_EQ(OK, GetReturnError(sink2->authenticationComplete(
+ source_info2.signature, sink2_info.sharedKeys, &auth_complete_result2)));
+ ASSERT_EQ((int)auth_complete_result2.size(), 2)
+ << "Expect two symmetric keys from authComplete()";
+ sink2_info.sharedKeys = auth_complete_result2;
+}
+
+TEST_P(AuthGraphSessionTest, ParallelSource) {
+ std::shared_ptr<IAuthGraphKeyExchange> source1 = authNode_;
+ std::shared_ptr<IAuthGraphKeyExchange> source2 = authNode_;
+ std::shared_ptr<IAuthGraphKeyExchange> sink = authNode_;
+
+ // Step 1: create an ephemeral ECDH key at each of the sources.
+ SessionInitiationInfo source1_init_info;
+ ASSERT_EQ(OK, GetReturnError(source1->create(&source1_init_info)));
+ ASSERT_TRUE(source1_init_info.key.pubKey.has_value());
+ ASSERT_TRUE(source1_init_info.key.arcFromPBK.has_value());
+ SessionInitiationInfo source2_init_info;
+ ASSERT_EQ(OK, GetReturnError(source1->create(&source2_init_info)));
+ ASSERT_TRUE(source2_init_info.key.pubKey.has_value());
+ ASSERT_TRUE(source2_init_info.key.arcFromPBK.has_value());
+
+ // Step 2: pass each source's ECDH public key and other session info to the sink.
+ KeInitResult init1_result;
+ ASSERT_EQ(OK, GetReturnError(sink->init(source1_init_info.key.pubKey.value(),
+ source1_init_info.identity, source1_init_info.nonce,
+ source1_init_info.version, &init1_result)));
+ SessionInitiationInfo sink_init1_info = init1_result.sessionInitiationInfo;
+ ASSERT_TRUE(sink_init1_info.key.pubKey.has_value());
+
+ SessionInfo sink_info1 = init1_result.sessionInfo;
+ ASSERT_EQ((int)sink_info1.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+ ASSERT_GT((int)sink_info1.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+ std::vector<uint8_t> sink_signing_key1 = SigningKeyFromIdentity(sink_init1_info.identity);
+ CheckSignature(sink_signing_key1, sink_info1.sessionId, sink_info1.signature);
+
+ KeInitResult init2_result;
+ ASSERT_EQ(OK, GetReturnError(sink->init(source2_init_info.key.pubKey.value(),
+ source2_init_info.identity, source2_init_info.nonce,
+ source2_init_info.version, &init2_result)));
+ SessionInitiationInfo sink_init2_info = init2_result.sessionInitiationInfo;
+ ASSERT_TRUE(sink_init2_info.key.pubKey.has_value());
+
+ SessionInfo sink_info2 = init2_result.sessionInfo;
+ ASSERT_EQ((int)sink_info2.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+ ASSERT_GT((int)sink_info2.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+ std::vector<uint8_t> sink_signing_key2 = SigningKeyFromIdentity(sink_init2_info.identity);
+ CheckSignature(sink_signing_key2, sink_info2.sessionId, sink_info2.signature);
+
+ // Step 3: pass the sink's ECDH public keys and other session info to the each of the sources.
+ SessionInfo source1_info;
+ ASSERT_EQ(OK, GetReturnError(source1->finish(sink_init1_info.key.pubKey.value(),
+ sink_init1_info.identity, sink_info1.signature,
+ sink_init1_info.nonce, sink_init1_info.version,
+ source1_init_info.key, &source1_info)));
+ ASSERT_EQ((int)source1_info.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+ ASSERT_GT((int)source1_info.sessionId.size(), 0) << "Expect non-empty session ID from source";
+ std::vector<uint8_t> source1_signing_key = SigningKeyFromIdentity(source1_init_info.identity);
+ CheckSignature(source1_signing_key, source1_info.sessionId, source1_info.signature);
+
+ SessionInfo source2_info;
+ ASSERT_EQ(OK, GetReturnError(source2->finish(sink_init2_info.key.pubKey.value(),
+ sink_init2_info.identity, sink_info2.signature,
+ sink_init2_info.nonce, sink_init2_info.version,
+ source2_init_info.key, &source2_info)));
+ ASSERT_EQ((int)source2_info.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+ ASSERT_GT((int)source2_info.sessionId.size(), 0) << "Expect non-empty session ID from source";
+ std::vector<uint8_t> source2_signing_key = SigningKeyFromIdentity(source2_init_info.identity);
+ CheckSignature(source2_signing_key, source2_info.sessionId, source2_info.signature);
+
+ // Both ends should agree on the session ID.
+ ASSERT_EQ(source1_info.sessionId, sink_info1.sessionId);
+ ASSERT_EQ(source2_info.sessionId, sink_info2.sessionId);
+
+ // Step 4: pass the each source's session ID info back to the sink, so it can check it and
+ // update the symmetric keys so they're marked as authentication complete.
+ std::array<Arc, 2> auth_complete_result1;
+ ASSERT_EQ(OK, GetReturnError(sink->authenticationComplete(
+ source1_info.signature, sink_info1.sharedKeys, &auth_complete_result1)));
+ ASSERT_EQ((int)auth_complete_result1.size(), 2)
+ << "Expect two symmetric keys from authComplete()";
+ sink_info1.sharedKeys = auth_complete_result1;
+ std::array<Arc, 2> auth_complete_result2;
+ ASSERT_EQ(OK, GetReturnError(sink->authenticationComplete(
+ source2_info.signature, sink_info2.sharedKeys, &auth_complete_result2)));
+ ASSERT_EQ((int)auth_complete_result2.size(), 2)
+ << "Expect two symmetric keys from authComplete()";
+ sink_info2.sharedKeys = auth_complete_result2;
+}
+
+TEST_P(AuthGraphSessionTest, FreshNonces) {
+ std::shared_ptr<IAuthGraphKeyExchange> source = authNode_;
+ std::shared_ptr<IAuthGraphKeyExchange> sink = authNode_;
+
+ SessionInitiationInfo source_init_info1;
+ ASSERT_EQ(OK, GetReturnError(source->create(&source_init_info1)));
+ SessionInitiationInfo source_init_info2;
+ ASSERT_EQ(OK, GetReturnError(source->create(&source_init_info2)));
+
+ // Two calls to create() should result in the same identity but different nonce values.
+ ASSERT_EQ(source_init_info1.identity, source_init_info2.identity);
+ ASSERT_NE(source_init_info1.nonce, source_init_info2.nonce);
+ ASSERT_NE(source_init_info1.key.pubKey, source_init_info2.key.pubKey);
+ ASSERT_NE(source_init_info1.key.arcFromPBK, source_init_info2.key.arcFromPBK);
+
+ KeInitResult init_result1;
+ ASSERT_EQ(OK, GetReturnError(sink->init(source_init_info1.key.pubKey.value(),
+ source_init_info1.identity, source_init_info1.nonce,
+ source_init_info1.version, &init_result1)));
+ KeInitResult init_result2;
+ ASSERT_EQ(OK, GetReturnError(sink->init(source_init_info2.key.pubKey.value(),
+ source_init_info2.identity, source_init_info2.nonce,
+ source_init_info2.version, &init_result2)));
+
+ // Two calls to init() should result in the same identity buf different nonces and session IDs.
+ ASSERT_EQ(init_result1.sessionInitiationInfo.identity,
+ init_result2.sessionInitiationInfo.identity);
+ ASSERT_NE(init_result1.sessionInitiationInfo.nonce, init_result2.sessionInitiationInfo.nonce);
+ ASSERT_NE(init_result1.sessionInfo.sessionId, init_result2.sessionInfo.sessionId);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, AuthGraphSessionTest,
+ testing::ValuesIn(AuthGraphSessionTest::build_params()),
+ ::android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AuthGraphSessionTest);
+
+} // namespace aidl::android::hardware::security::authgraph::test
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/security/authgraph/default/Android.bp b/security/authgraph/default/Android.bp
new file mode 100644
index 0000000..9de3bc1
--- /dev/null
+++ b/security/authgraph/default/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+rust_binary {
+ name: "android.hardware.security.authgraph-service.nonsecure",
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["authgraph.rc"],
+ vintf_fragments: ["authgraph.xml"],
+ defaults: [
+ "authgraph_use_latest_hal_aidl_rust",
+ ],
+ rustlibs: [
+ "libandroid_logger",
+ "libauthgraph_core",
+ "libauthgraph_boringssl",
+ "libauthgraph_hal",
+ "libbinder_rs",
+ "liblibc",
+ "liblog_rust",
+ ],
+ srcs: [
+ "src/main.rs",
+ ],
+}
diff --git a/security/authgraph/default/authgraph.rc b/security/authgraph/default/authgraph.rc
new file mode 100644
index 0000000..0222994
--- /dev/null
+++ b/security/authgraph/default/authgraph.rc
@@ -0,0 +1,5 @@
+service vendor.authgraph /vendor/bin/hw/android.hardware.security.authgraph-service.nonsecure
+ interface aidl android.hardware.security.authgraph.IAuthGraph/nonsecure
+ class hal
+ user nobody
+ group nobody
diff --git a/security/authgraph/default/authgraph.xml b/security/authgraph/default/authgraph.xml
new file mode 100644
index 0000000..9529a0a
--- /dev/null
+++ b/security/authgraph/default/authgraph.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.security.authgraph</name>
+ <version>1</version>
+ <interface>
+ <name>IAuthGraphKeyExchange</name>
+ <instance>nonsecure</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/security/authgraph/default/src/main.rs b/security/authgraph/default/src/main.rs
new file mode 100644
index 0000000..2112e58
--- /dev/null
+++ b/security/authgraph/default/src/main.rs
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Default implementation of the AuthGraph key exchange HAL.
+//!
+//! This implementation of the HAL is only intended to allow testing and policy compliance. A real
+//! implementation of the AuthGraph HAL would be implemented in a secure environment, and would not
+//! be independently registered with service manager (a secure component that uses AuthGraph would
+//! expose an entrypoint that allowed retrieval of the specific IAuthGraphKeyExchange instance that
+//! is correlated with the component).
+
+use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
+ Arc::Arc, IAuthGraphKeyExchange::BnAuthGraphKeyExchange,
+ IAuthGraphKeyExchange::IAuthGraphKeyExchange, Identity::Identity, KeInitResult::KeInitResult,
+ Key::Key, PubKey::PubKey, SessionIdSignature::SessionIdSignature, SessionInfo::SessionInfo,
+ SessionInitiationInfo::SessionInitiationInfo,
+};
+use authgraph_boringssl as boring;
+use authgraph_core::{key::MillisecondsSinceEpoch, keyexchange as ke, traits};
+use authgraph_hal::{err_to_binder, Innto, TryInnto};
+use log::{error, info};
+use std::ffi::CString;
+use std::sync::Mutex;
+
+static SERVICE_NAME: &str = "android.hardware.security.authgraph.IAuthGraphKeyExchange";
+static SERVICE_INSTANCE: &str = "nonsecure";
+
+/// Local error type for failures in the HAL service.
+#[derive(Debug, Clone)]
+struct HalServiceError(String);
+
+impl From<String> for HalServiceError {
+ fn from(s: String) -> Self {
+ Self(s)
+ }
+}
+
+fn main() {
+ if let Err(e) = inner_main() {
+ panic!("HAL service failed: {:?}", e);
+ }
+}
+
+fn inner_main() -> Result<(), HalServiceError> {
+ // Initialize Android logging.
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("authgraph-hal-nonsecure")
+ .with_min_level(log::Level::Info)
+ .with_log_id(android_logger::LogId::System),
+ );
+ // Redirect panic messages to logcat.
+ std::panic::set_hook(Box::new(|panic_info| {
+ error!("{}", panic_info);
+ }));
+
+ info!("Insecure AuthGraph key exchange HAL service is starting.");
+
+ info!("Starting thread pool now.");
+ binder::ProcessState::start_thread_pool();
+
+ // Register the service
+ let service = AuthGraphService::new_as_binder();
+ let service_name = format!("{}/{}", SERVICE_NAME, SERVICE_INSTANCE);
+ binder::add_service(&service_name, service.as_binder()).map_err(|e| {
+ format!(
+ "Failed to register service {} because of {:?}.",
+ service_name, e
+ )
+ })?;
+
+ info!("Successfully registered AuthGraph HAL services.");
+ binder::ProcessState::join_thread_pool();
+ info!("AuthGraph HAL service is terminating."); // should not reach here
+ Ok(())
+}
+
+/// Non-secure implementation of the AuthGraph key exchange service.
+struct AuthGraphService {
+ imp: Mutex<traits::TraitImpl>,
+}
+
+impl AuthGraphService {
+ /// Create a new instance.
+ fn new() -> Self {
+ Self {
+ imp: Mutex::new(traits::TraitImpl {
+ aes_gcm: Box::new(boring::BoringAes),
+ ecdh: Box::new(boring::BoringEcDh),
+ ecdsa: Box::new(boring::BoringEcDsa),
+ hmac: Box::new(boring::BoringHmac),
+ hkdf: Box::new(boring::BoringHkdf),
+ sha256: Box::new(boring::BoringSha256),
+ rng: Box::new(boring::BoringRng),
+ device: Box::<boring::test_device::AgDevice>::default(),
+ clock: Some(Box::new(StdClock)),
+ }),
+ }
+ }
+
+ /// Create a new instance wrapped in a proxy object.
+ pub fn new_as_binder() -> binder::Strong<dyn IAuthGraphKeyExchange> {
+ BnAuthGraphKeyExchange::new_binder(Self::new(), binder::BinderFeatures::default())
+ }
+}
+
+impl binder::Interface for AuthGraphService {}
+
+/// Extract (and require) an unsigned public key as bytes from a [`PubKey`].
+fn unsigned_pub_key(pub_key: &PubKey) -> binder::Result<&[u8]> {
+ match pub_key {
+ PubKey::PlainKey(key) => Ok(&key.plainPubKey),
+ PubKey::SignedKey(_) => Err(binder::Status::new_exception(
+ binder::ExceptionCode::ILLEGAL_ARGUMENT,
+ Some(&CString::new("expected unsigned public key").unwrap()),
+ )),
+ }
+}
+
+/// This nonsecure implementation of the AuthGraph HAL interface directly calls the AuthGraph
+/// reference implementation library code; a real implementation requires the AuthGraph
+/// code to run in a secure environment, not within Android.
+impl IAuthGraphKeyExchange for AuthGraphService {
+ fn create(&self) -> binder::Result<SessionInitiationInfo> {
+ info!("create()");
+ let mut imp = self.imp.lock().unwrap();
+ let info = ke::create(&mut *imp).map_err(err_to_binder)?;
+ Ok(info.innto())
+ }
+ fn init(
+ &self,
+ peer_pub_key: &PubKey,
+ peer_id: &Identity,
+ peer_nonce: &[u8],
+ peer_version: i32,
+ ) -> binder::Result<KeInitResult> {
+ info!("init(v={peer_version})");
+ let mut imp = self.imp.lock().unwrap();
+ let peer_pub_key = unsigned_pub_key(peer_pub_key)?;
+ let result = ke::init(
+ &mut *imp,
+ peer_pub_key,
+ &peer_id.identity,
+ &peer_nonce,
+ peer_version,
+ )
+ .map_err(err_to_binder)?;
+ Ok(result.innto())
+ }
+
+ fn finish(
+ &self,
+ peer_pub_key: &PubKey,
+ peer_id: &Identity,
+ peer_signature: &SessionIdSignature,
+ peer_nonce: &[u8],
+ peer_version: i32,
+ own_key: &Key,
+ ) -> binder::Result<SessionInfo> {
+ info!("finish(v={peer_version})");
+ let mut imp = self.imp.lock().unwrap();
+ let peer_pub_key = unsigned_pub_key(peer_pub_key)?;
+ let own_key: Key = own_key.clone();
+ let own_key: authgraph_core::key::Key = own_key.try_innto()?;
+ let session_info = ke::finish(
+ &mut *imp,
+ peer_pub_key,
+ &peer_id.identity,
+ &peer_signature.signature,
+ &peer_nonce,
+ peer_version,
+ own_key,
+ )
+ .map_err(err_to_binder)?;
+ Ok(session_info.innto())
+ }
+
+ fn authenticationComplete(
+ &self,
+ peer_signature: &SessionIdSignature,
+ shared_keys: &[Arc; 2],
+ ) -> binder::Result<[Arc; 2]> {
+ info!("authComplete()");
+ let mut imp = self.imp.lock().unwrap();
+ let shared_keys = [shared_keys[0].arc.clone(), shared_keys[1].arc.clone()];
+ let arcs = ke::authentication_complete(&mut *imp, &peer_signature.signature, shared_keys)
+ .map_err(err_to_binder)?;
+ Ok(arcs.map(|arc| Arc { arc }))
+ }
+}
+
+/// Monotonic clock.
+#[derive(Default)]
+pub struct StdClock;
+
+impl traits::MonotonicClock for StdClock {
+ fn now(&self) -> authgraph_core::key::MillisecondsSinceEpoch {
+ let mut time = libc::timespec {
+ tv_sec: 0, // libc::time_t
+ tv_nsec: 0, // libc::c_long
+ };
+ let rc =
+ // Safety: `time` is a valid structure.
+ unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut time as *mut libc::timespec) };
+ if rc < 0 {
+ log::warn!("failed to get time!");
+ return MillisecondsSinceEpoch(0);
+ }
+ // The types in `libc::timespec` may be different on different architectures,
+ // so allow conversion to `i64`.
+ #[allow(clippy::unnecessary_cast)]
+ MillisecondsSinceEpoch((time.tv_sec as i64 * 1000) + (time.tv_nsec as i64 / 1000 / 1000))
+ }
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index 36f0106..aa7bf28 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -643,6 +643,8 @@
* Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attested key
* generation/import methods, which must place the value in the KeyDescription SEQUENCE of the
* attestation extension.
+ * The challenge value may be up to 128 bytes. If the caller provides a bigger challenge,
+ * INVALID_INPUT_LENGTH error should be returned.
*
* Must never appear in KeyCharacteristics.
*/
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.cpp b/tv/tuner/aidl/vts/functional/FilterTests.cpp
index 53afef7..533d0e6 100644
--- a/tv/tuner/aidl/vts/functional/FilterTests.cpp
+++ b/tv/tuner/aidl/vts/functional/FilterTests.cpp
@@ -305,13 +305,18 @@
ndk::ScopedAStatus status;
status = mFilters[filterId]->configureMonitorEvent(monitorEventTypes);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::testMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes) {
+ EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
if (monitorEventTypes & static_cast<int32_t>(DemuxFilterMonitorEventType::SCRAMBLING_STATUS)) {
mFilterCallbacks[filterId]->testFilterScramblingEvent();
}
if (monitorEventTypes & static_cast<int32_t>(DemuxFilterMonitorEventType::IP_CID_CHANGE)) {
mFilterCallbacks[filterId]->testFilterIpCidEvent();
}
- return AssertionResult(status.isOk());
+ return AssertionResult(true);
}
AssertionResult FilterTests::startIdTest(int64_t filterId) {
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.h b/tv/tuner/aidl/vts/functional/FilterTests.h
index f579441..f57093e 100644
--- a/tv/tuner/aidl/vts/functional/FilterTests.h
+++ b/tv/tuner/aidl/vts/functional/FilterTests.h
@@ -124,6 +124,7 @@
AssertionResult configAvFilterStreamType(AvStreamType type, int64_t filterId);
AssertionResult configIpFilterCid(int32_t ipCid, int64_t filterId);
AssertionResult configureMonitorEvent(int64_t filterId, int32_t monitorEventTypes);
+ AssertionResult testMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes);
AssertionResult getFilterMQDescriptor(int64_t filterId, bool getMqDesc);
AssertionResult startFilter(int64_t filterId);
AssertionResult stopFilter(int64_t filterId);
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
index 9db82c8..3664b6c 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -60,6 +60,11 @@
}
ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ if (filterConf.monitorEventTypes > 0) {
+ ASSERT_TRUE(mFilterTests.testMonitorEvent(filterId, filterConf.monitorEventTypes));
+ }
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
ASSERT_TRUE(mDemuxTests.closeDemux());