Merge "Add vts case for OverlayProperties#isMixedColorSpacesSupported." into main
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 41fc80b..844f1e9 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -249,7 +249,7 @@
"EffectFactory.cpp",
"EffectMain.cpp",
],
- installable: false, //installed in apex com.android.hardware.audio.effect
+ installable: false, //installed in apex com.android.hardware.audio
}
cc_library_headers {
@@ -271,9 +271,3 @@
sub_dir: "vintf",
installable: false,
}
-
-prebuilt_etc {
- name: "audio_effects_config.xml",
- src: "audio_effects_config.xml",
- installable: false,
-}
diff --git a/audio/aidl/default/apex/com.android.hardware.audio/Android.bp b/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
index 2ece7a1..ee7e46e 100644
--- a/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
+++ b/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
@@ -46,6 +46,5 @@
prebuilts: [
"android.hardware.audio.service-aidl.example.rc",
"android.hardware.audio.service-aidl.xml",
- "audio_effects_config.xml",
],
}
diff --git a/audio/aidl/default/visualizer/VisualizerSw.h b/audio/aidl/default/visualizer/VisualizerSw.h
index 4b87b04..819351a 100644
--- a/audio/aidl/default/visualizer/VisualizerSw.h
+++ b/audio/aidl/default/visualizer/VisualizerSw.h
@@ -19,20 +19,22 @@
#include <vector>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <system/audio_effects/effect_visualizer.h>
#include "effect-impl/EffectImpl.h"
namespace aidl::android::hardware::audio::effect {
class VisualizerSwContext final : public EffectContext {
public:
- static const int kMinCaptureSize = 0x80;
- static const int kMaxCaptureSize = 0x400;
- static const int kMaxLatencyMs = 3000;
- static const int kMaxCaptureBufSize = 0xffff;
+ // need align the min/max capture size to VISUALIZER_CAPTURE_SIZE_MIN and
+ // VISUALIZER_CAPTURE_SIZE_MAX because of limitation in audio_utils fixedfft.
+ static constexpr int32_t kMinCaptureSize = VISUALIZER_CAPTURE_SIZE_MIN;
+ static constexpr int32_t kMaxCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
+ static constexpr int32_t kMaxLatencyMs = 3000;
VisualizerSwContext(int statusDepth, const Parameter::Common& common)
: EffectContext(statusDepth, common) {
LOG(DEBUG) << __func__;
- mCaptureSampleBuffer.resize(kMaxCaptureBufSize);
+ mCaptureSampleBuffer.resize(kMaxCaptureSize);
fill(mCaptureSampleBuffer.begin(), mCaptureSampleBuffer.end(), 0x80);
}
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 5218fdd..d219fa4 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -26,6 +26,7 @@
"android.hardware.common.fmq-V1-ndk",
"libaudioaidlcommon",
"libaidlcommonsupport",
+ "libpffft",
],
header_libs: [
"libaudioaidl_headers",
@@ -36,6 +37,7 @@
"-Wextra",
"-Werror",
"-Wthread-safety",
+ "-Wno-error=unused-parameter",
],
test_config_template: "VtsHalAudioTargetTestTemplate.xml",
test_suites: [
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index 0be4e50..82a07fd 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -37,6 +37,7 @@
#include "EffectFactoryHelper.h"
#include "TestUtils.h"
+#include "pffft.hpp"
using namespace android;
using aidl::android::hardware::audio::effect::CommandId;
@@ -329,4 +330,45 @@
ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET));
ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
}
+
+ // Find FFT bin indices for testFrequencies and get bin center frequencies
+ void roundToFreqCenteredToFftBin(std::vector<int>& testFrequencies,
+ std::vector<int>& binOffsets, const float kBinWidth) {
+ for (size_t i = 0; i < testFrequencies.size(); i++) {
+ binOffsets[i] = std::round(testFrequencies[i] / kBinWidth);
+ testFrequencies[i] = std::round(binOffsets[i] * kBinWidth);
+ }
+ }
+
+ // Generate multitone input between -1 to +1 using testFrequencies
+ void generateMultiTone(const std::vector<int>& testFrequencies, std::vector<float>& input,
+ const int samplingFrequency) {
+ for (size_t i = 0; i < input.size(); i++) {
+ input[i] = 0;
+
+ for (size_t j = 0; j < testFrequencies.size(); j++) {
+ input[i] += sin(2 * M_PI * testFrequencies[j] * i / samplingFrequency);
+ }
+ input[i] /= testFrequencies.size();
+ }
+ }
+
+ // Use FFT transform to convert the buffer to frequency domain
+ // Compute its magnitude at binOffsets
+ std::vector<float> calculateMagnitude(const std::vector<float>& buffer,
+ const std::vector<int>& binOffsets, const int nPointFFT) {
+ std::vector<float> fftInput(nPointFFT);
+ PFFFT_Setup* inputHandle = pffft_new_setup(nPointFFT, PFFFT_REAL);
+ pffft_transform_ordered(inputHandle, buffer.data(), fftInput.data(), nullptr,
+ PFFFT_FORWARD);
+ pffft_destroy_setup(inputHandle);
+ std::vector<float> bufferMag(binOffsets.size());
+ for (size_t i = 0; i < binOffsets.size(); i++) {
+ size_t k = binOffsets[i];
+ bufferMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
+ (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
+ }
+
+ return bufferMag;
+ }
};
diff --git a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
index aa2c05f..059d6ab 100644
--- a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
@@ -21,6 +21,7 @@
using namespace android;
+using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::getEffectTypeUuidVolume;
using aidl::android::hardware::audio::effect::IEffect;
@@ -29,6 +30,80 @@
using aidl::android::hardware::audio::effect::Volume;
using android::hardware::audio::common::testing::detail::TestExecutionTracer;
+class VolumeControlHelper : public EffectHelper {
+ public:
+ void SetUpVolumeControl() {
+ ASSERT_NE(nullptr, mFactory);
+ ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+ initFrameCount();
+ Parameter::Specific specific = getDefaultParamSpecific();
+ Parameter::Common common = EffectHelper::createParamCommon(
+ 0 /* session */, 1 /* ioHandle */, kSamplingFrequency /* iSampleRate */,
+ kSamplingFrequency /* oSampleRate */, mInputFrameCount /* iFrameCount */,
+ mInputFrameCount /* oFrameCount */);
+ ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE));
+ ASSERT_NE(nullptr, mEffect);
+ }
+
+ void TearDownVolumeControl() {
+ ASSERT_NO_FATAL_FAILURE(close(mEffect));
+ ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+ mOpenEffectReturn = IEffect::OpenEffectReturn{};
+ }
+
+ Parameter::Specific getDefaultParamSpecific() {
+ Volume vol = Volume::make<Volume::levelDb>(kMinLevel);
+ Parameter::Specific specific = Parameter::Specific::make<Parameter::Specific::volume>(vol);
+ return specific;
+ }
+
+ Parameter createVolumeParam(int param, Volume::Tag volTag) {
+ return Parameter::make<Parameter::specific>(
+ Parameter::Specific::make<Parameter::Specific::volume>(
+ (volTag == Volume::mute) ? Volume::make<Volume::mute>(param)
+ : Volume::make<Volume::levelDb>(param)));
+ }
+
+ void initFrameCount() {
+ int channelCount = getChannelCount(
+ AudioChannelLayout::make<AudioChannelLayout::layoutMask>(kDefaultChannelLayout));
+ mInputFrameCount = kBufferSize / channelCount;
+ mOutputFrameCount = kBufferSize / channelCount;
+ }
+
+ bool isLevelValid(int level) {
+ auto vol = Volume::make<Volume::levelDb>(level);
+ return isParameterValid<Volume, Range::volume>(vol, mDescriptor);
+ }
+
+ void setAndVerifyParameters(Volume::Tag volTag, int param, binder_exception_t expected) {
+ auto expectedParam = createVolumeParam(param, volTag);
+ EXPECT_STATUS(expected, mEffect->setParameter(expectedParam)) << expectedParam.toString();
+
+ if (expected == EX_NONE) {
+ Volume::Id volId = Volume::Id::make<Volume::Id::commonTag>(volTag);
+
+ auto id = Parameter::Id::make<Parameter::Id::volumeTag>(volId);
+ // get parameter
+ Parameter getParam;
+ // if set success, then get should match
+ EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam));
+ EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString()
+ << "\ngetParam:" << getParam.toString();
+ }
+ }
+
+ static constexpr int kSamplingFrequency = 44100;
+ static constexpr int kDurationMilliSec = 2000;
+ static constexpr int kBufferSize = kSamplingFrequency * kDurationMilliSec / 1000;
+ static constexpr int kMinLevel = -96;
+ static constexpr int kDefaultChannelLayout = AudioChannelLayout::LAYOUT_STEREO;
+ long mInputFrameCount, mOutputFrameCount;
+ std::shared_ptr<IFactory> mFactory;
+ std::shared_ptr<IEffect> mEffect;
+ IEffect::OpenEffectReturn mOpenEffectReturn;
+ Descriptor mDescriptor;
+};
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
* VtsAudioEffectTargetTest.
@@ -37,7 +112,8 @@
using VolumeParamTestParam =
std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int, bool>;
-class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>, public EffectHelper {
+class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>,
+ public VolumeControlHelper {
public:
VolumeParamTest()
: mParamLevel(std::get<PARAM_LEVEL>(GetParam())),
@@ -45,94 +121,167 @@
std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
}
- void SetUp() override {
- ASSERT_NE(nullptr, mFactory);
- ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+ void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); }
+ void TearDown() override { TearDownVolumeControl(); }
- Parameter::Specific specific = getDefaultParamSpecific();
- 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_NE(nullptr, mEffect);
- }
- void TearDown() override {
- ASSERT_NO_FATAL_FAILURE(close(mEffect));
- ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
- }
-
- Parameter::Specific getDefaultParamSpecific() {
- Volume vol = Volume::make<Volume::levelDb>(-9600);
- Parameter::Specific specific = Parameter::Specific::make<Parameter::Specific::volume>(vol);
- return specific;
- }
-
- static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
- std::shared_ptr<IFactory> mFactory;
- std::shared_ptr<IEffect> mEffect;
- Descriptor mDescriptor;
int mParamLevel = 0;
bool mParamMute = false;
-
- void SetAndGetParameters() {
- for (auto& it : mTags) {
- auto& tag = it.first;
- auto& vol = it.second;
-
- // validate parameter
- Descriptor desc;
- ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
- const bool valid = isParameterValid<Volume, Range::volume>(it.second, desc);
- const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
-
- // set parameter
- Parameter expectParam;
- Parameter::Specific specific;
- specific.set<Parameter::Specific::volume>(vol);
- expectParam.set<Parameter::specific>(specific);
- EXPECT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString();
-
- // only get if parameter is in range and set success
- if (expected == EX_NONE) {
- Parameter getParam;
- Parameter::Id id;
- Volume::Id volId;
- volId.set<Volume::Id::commonTag>(tag);
- id.set<Parameter::Id::volumeTag>(volId);
- EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
-
- EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString()
- << "\ngetParam:" << getParam.toString();
- }
- }
- }
-
- void addLevelParam(int level) {
- Volume vol;
- vol.set<Volume::levelDb>(level);
- mTags.push_back({Volume::levelDb, vol});
- }
-
- void addMuteParam(bool mute) {
- Volume vol;
- vol.set<Volume::mute>(mute);
- mTags.push_back({Volume::mute, vol});
- }
-
- private:
- std::vector<std::pair<Volume::Tag, Volume>> mTags;
- void CleanUp() { mTags.clear(); }
};
-TEST_P(VolumeParamTest, SetAndGetLevel) {
- EXPECT_NO_FATAL_FAILURE(addLevelParam(mParamLevel));
- SetAndGetParameters();
+TEST_P(VolumeParamTest, SetAndGetParams) {
+ ASSERT_NO_FATAL_FAILURE(
+ setAndVerifyParameters(Volume::levelDb, mParamLevel,
+ isLevelValid(mParamLevel) ? EX_NONE : EX_ILLEGAL_ARGUMENT));
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, mParamMute, EX_NONE));
}
-TEST_P(VolumeParamTest, SetAndGetMute) {
- EXPECT_NO_FATAL_FAILURE(addMuteParam(mParamMute));
- SetAndGetParameters();
+using VolumeDataTestParam = std::pair<std::shared_ptr<IFactory>, Descriptor>;
+
+class VolumeDataTest : public ::testing::TestWithParam<VolumeDataTestParam>,
+ public VolumeControlHelper {
+ public:
+ VolumeDataTest() {
+ std::tie(mFactory, mDescriptor) = GetParam();
+ mInput.resize(kBufferSize);
+ mInputMag.resize(mTestFrequencies.size());
+ mBinOffsets.resize(mTestFrequencies.size());
+ roundToFreqCenteredToFftBin(mTestFrequencies, mBinOffsets, kBinWidth);
+ generateMultiTone(mTestFrequencies, mInput, kSamplingFrequency);
+ mInputMag = calculateMagnitude(mInput, mBinOffsets, kNPointFFT);
+ }
+
+ std::vector<int> calculatePercentageDiff(const std::vector<float>& outputMag) {
+ std::vector<int> percentages(mTestFrequencies.size());
+
+ for (size_t i = 0; i < mInputMag.size(); i++) {
+ float diff = mInputMag[i] - outputMag[i];
+ percentages[i] = std::round(diff / mInputMag[i] * 100);
+ }
+ return percentages;
+ }
+
+ // Convert Decibel value to Percentage
+ int percentageDb(float level) { return std::round((1 - (pow(10, level / 20))) * 100); }
+
+ void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); }
+ void TearDown() override { TearDownVolumeControl(); }
+
+ static constexpr int kMaxAudioSample = 1;
+ static constexpr int kTransitionDuration = 300;
+ static constexpr int kNPointFFT = 32768;
+ static constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT;
+ static constexpr size_t offset = kSamplingFrequency * kTransitionDuration / 1000;
+ static constexpr float kBaseLevel = 0;
+ std::vector<int> mTestFrequencies = {100, 1000};
+ std::vector<float> mInput;
+ std::vector<float> mInputMag;
+ std::vector<int> mBinOffsets;
+};
+
+TEST_P(VolumeDataTest, ApplyLevelMuteUnmute) {
+ std::vector<float> output(kBufferSize);
+ std::vector<int> diffs(mTestFrequencies.size());
+ std::vector<float> outputMag(mTestFrequencies.size());
+
+ if (!isLevelValid(kBaseLevel)) {
+ GTEST_SKIP() << "Volume Level not supported, skipping the test\n";
+ }
+
+ // Apply Volume Level
+
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+ outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_EQ(diffs[i], percentageDb(kBaseLevel));
+ }
+
+ // Apply Mute
+
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, true /*mute*/, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+ std::vector<float> subOutputMute(output.begin() + offset, output.end());
+ outputMag = calculateMagnitude(subOutputMute, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_EQ(diffs[i], percentageDb(kMinLevel /*Mute*/));
+ }
+
+ // Verifying Fade out
+ outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_LT(diffs[i], percentageDb(kMinLevel /*Mute*/));
+ }
+
+ // Apply Unmute
+
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, false /*unmute*/, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+ std::vector<float> subOutputUnmute(output.begin() + offset, output.end());
+
+ outputMag = calculateMagnitude(subOutputUnmute, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_EQ(diffs[i], percentageDb(kBaseLevel));
+ }
+
+ // Verifying Fade in
+ outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_GT(diffs[i], percentageDb(kBaseLevel));
+ }
+}
+
+TEST_P(VolumeDataTest, DecreasingLevels) {
+ std::vector<int> decreasingLevels = {-24, -48, -96};
+ std::vector<float> baseOutput(kBufferSize);
+ std::vector<int> baseDiffs(mTestFrequencies.size());
+ std::vector<float> outputMag(mTestFrequencies.size());
+
+ if (!isLevelValid(kBaseLevel)) {
+ GTEST_SKIP() << "Volume Level not supported, skipping the test\n";
+ }
+
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(
+ processAndWriteToOutput(mInput, baseOutput, mEffect, &mOpenEffectReturn));
+
+ outputMag = calculateMagnitude(baseOutput, mBinOffsets, kNPointFFT);
+ baseDiffs = calculatePercentageDiff(outputMag);
+
+ for (int level : decreasingLevels) {
+ std::vector<float> output(kBufferSize);
+ std::vector<int> diffs(mTestFrequencies.size());
+
+ // Skipping the further steps for unnsupported level values
+ if (!isLevelValid(level)) {
+ continue;
+ }
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, level, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(
+ processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+ outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ // Decrease in volume level results in greater magnitude difference
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_GT(diffs[i], baseDiffs[i]);
+ }
+
+ baseDiffs = diffs;
+ }
}
std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
@@ -157,6 +306,20 @@
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VolumeParamTest);
+INSTANTIATE_TEST_SUITE_P(VolumeTest, VolumeDataTest,
+ testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+ IFactory::descriptor, getEffectTypeUuidVolume())),
+ [](const testing::TestParamInfo<VolumeDataTest::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(VolumeDataTest);
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
diff --git a/automotive/remoteaccess/hal/default/Android.bp b/automotive/remoteaccess/hal/default/Android.bp
index be6a425..cf173d5 100644
--- a/automotive/remoteaccess/hal/default/Android.bp
+++ b/automotive/remoteaccess/hal/default/Android.bp
@@ -52,11 +52,6 @@
defaults: ["remote-access-hal-defaults"],
vintf_fragments: ["remoteaccess-default-service.xml"],
init_rc: ["remoteaccess-default-service.rc"],
- cflags: [
- // Uncomment this if running on emulator and connecting to a local grpc server
- // running on host 127.0.0.1:50051 (TestWakeupClientServerHost)
- // "-DGRPC_SERVICE_ADDRESS=\"10.0.2.2:50051\"",
- ],
}
cc_binary {
@@ -64,10 +59,6 @@
defaults: ["remote-access-hal-defaults"],
vintf_fragments: ["remoteaccess-default-service.xml"],
init_rc: ["remoteaccess-tcu-test-service.rc"],
- cflags: [
- "-DGRPC_SERVICE_ADDRESS=\"10.10.10.1:50051\"",
- "-DGRPC_SERVICE_IFNAME=\"eth1\"",
- ],
}
cc_library {
diff --git a/automotive/remoteaccess/hal/default/proto/wakeup_client.proto b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
index 14ba0a5..8ff6059 100644
--- a/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
+++ b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
@@ -124,6 +124,11 @@
ErrorCode errorCode = 1;
}
+enum ScheduleTaskType {
+ CUSTOM = 0;
+ ENTER_GARAGE_MODE = 1;
+}
+
message GrpcScheduleInfo {
string clientId = 1;
string scheduleId = 2;
@@ -131,6 +136,7 @@
int32 count = 4;
int64 startTimeInEpochSeconds = 5;
int64 periodicInSeconds = 6;
+ ScheduleTaskType taskType = 7;
}
message UnscheduleTaskRequest {
@@ -162,3 +168,25 @@
message GetAllPendingScheduledTasksResponse {
repeated GrpcScheduleInfo allScheduledTasks = 1;
}
+
+/**
+ * Service provided by a power controller unit.
+ */
+service PowerController {
+ rpc IsVehicleInUse(IsVehicleInUseRequest) returns (IsVehicleInUseResponse) {}
+
+ rpc GetApPowerBootupReason(GetApPowerBootupReasonRequest)
+ returns (GetApPowerBootupReasonResponse) {}
+}
+
+message IsVehicleInUseRequest {}
+
+message IsVehicleInUseResponse {
+ bool isVehicleInUse = 1;
+}
+
+message GetApPowerBootupReasonRequest {}
+
+message GetApPowerBootupReasonResponse {
+ int32 bootupReason = 1;
+}
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
index 28c5cd5..a50f3bb 100644
--- a/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
@@ -27,37 +27,66 @@
#include <libnetdevice/libnetdevice.h>
#include <stdlib.h>
+namespace {
+
+constexpr char GRPC_SERVICE_CONFIG_FILE[] = "/vendor/etc/automotive/powercontroller/serverconfig";
constexpr char SERVICE_NAME[] = "android.hardware.automotive.remoteaccess.IRemoteAccess/default";
+void maybeGetGrpcServiceInfo(std::string* address, std::string* ifname) {
+ std::ifstream ifs(GRPC_SERVICE_CONFIG_FILE);
+ if (!ifs) {
+ LOG(INFO) << "Cannot open grpc service config file at: " << GRPC_SERVICE_CONFIG_FILE
+ << ", assume no service is available";
+ return;
+ }
+ int count = 0;
+ while (ifs.good()) {
+ std::string line;
+ ifs >> line;
+ // First line is address, second line, if present is ifname.
+ if (count == 0) {
+ *address = line;
+ } else {
+ *ifname = line;
+ break;
+ }
+ count++;
+ }
+ ifs.close();
+}
+
+} // namespace
+
int main(int /* argc */, char* /* argv */[]) {
- android::hardware::automotive::remoteaccess::WakeupClient::StubInterface* grpcStub = nullptr;
+ std::string grpcServiceAddress = "";
+ std::string grpcServiceIfname = "";
+ maybeGetGrpcServiceInfo(&grpcServiceAddress, &grpcServiceIfname);
-#ifdef GRPC_SERVICE_ADDRESS
- LOG(INFO) << "Registering RemoteAccessService as service, server: " << GRPC_SERVICE_ADDRESS
- << "...";
- grpc::ChannelArguments grpcargs = {};
+ std::unique_ptr<android::hardware::automotive::remoteaccess::WakeupClient::Stub> grpcStub;
-#ifdef GRPC_SERVICE_IFNAME
- grpcargs.SetSocketMutator(
- android::hardware::automotive::remoteaccess::MakeBindToDeviceSocketMutator(
- GRPC_SERVICE_IFNAME));
- LOG(DEBUG) << "GRPC_SERVICE_IFNAME specified as: " << GRPC_SERVICE_IFNAME;
- LOG(INFO) << "Waiting for interface: " << GRPC_SERVICE_IFNAME;
- android::netdevice::waitFor({GRPC_SERVICE_IFNAME},
- android::netdevice::WaitCondition::PRESENT_AND_UP);
- LOG(INFO) << "Waiting for interface: " << GRPC_SERVICE_IFNAME << " done";
-#endif // #ifdef GRPC_SERVICE_IFNAME
- auto channel = grpc::CreateChannel(GRPC_SERVICE_ADDRESS, grpc::InsecureChannelCredentials());
- auto clientStub = android::hardware::automotive::remoteaccess::WakeupClient::NewStub(channel);
+ if (grpcServiceAddress != "") {
+ LOG(INFO) << "Registering RemoteAccessService as service, server: " << grpcServiceAddress
+ << "...";
+ grpc::ChannelArguments grpcargs = {};
- grpcStub = clientStub.get();
-
-#else
- LOG(INFO) << "GRPC_SERVICE_ADDRESS is not defined, work in fake mode";
-#endif // #ifdef GRPC_SERVICE_ADDRESS
+ if (grpcServiceIfname != "") {
+ grpcargs.SetSocketMutator(
+ android::hardware::automotive::remoteaccess::MakeBindToDeviceSocketMutator(
+ grpcServiceIfname));
+ LOG(DEBUG) << "grpcServiceIfname specified as: " << grpcServiceIfname;
+ LOG(INFO) << "Waiting for interface: " << grpcServiceIfname;
+ android::netdevice::waitFor({grpcServiceIfname},
+ android::netdevice::WaitCondition::PRESENT_AND_UP);
+ LOG(INFO) << "Waiting for interface: " << grpcServiceIfname << " done";
+ }
+ auto channel = grpc::CreateChannel(grpcServiceAddress, grpc::InsecureChannelCredentials());
+ grpcStub = android::hardware::automotive::remoteaccess::WakeupClient::NewStub(channel);
+ } else {
+ LOG(INFO) << "grpcServiceAddress is not defined, work in fake mode";
+ }
auto service = ndk::SharedRefBase::make<
- android::hardware::automotive::remoteaccess::RemoteAccessService>(grpcStub);
+ android::hardware::automotive::remoteaccess::RemoteAccessService>(grpcStub.get());
binder_exception_t err = AServiceManager_addService(service->asBinder().get(), SERVICE_NAME);
if (err != EX_NONE) {
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
index dbd5bed..91689b1 100644
--- a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
@@ -346,8 +346,8 @@
return ScopedAStatus::ok();
}
- // TODO(b/316233421): support ENTER_GARAGE_MODE type.
out->push_back(TaskType::CUSTOM);
+ out->push_back(TaskType::ENTER_GARAGE_MODE);
return ScopedAStatus::ok();
}
@@ -380,6 +380,8 @@
}
request.mutable_scheduleinfo()->set_clientid(scheduleInfo.clientId);
+ request.mutable_scheduleinfo()->set_tasktype(
+ static_cast<ScheduleTaskType>(scheduleInfo.taskType));
request.mutable_scheduleinfo()->set_scheduleid(scheduleInfo.scheduleId);
request.mutable_scheduleinfo()->set_data(scheduleInfo.taskData.data(),
scheduleInfo.taskData.size());
@@ -485,6 +487,7 @@
const GrpcScheduleInfo& rpcScheduleInfo = response.allscheduledtasks(i);
ScheduleInfo scheduleInfo = {
.clientId = rpcScheduleInfo.clientid(),
+ .taskType = static_cast<TaskType>(rpcScheduleInfo.tasktype()),
.scheduleId = rpcScheduleInfo.scheduleid(),
.taskData = stringToBytes(rpcScheduleInfo.data()),
.count = rpcScheduleInfo.count(),
diff --git a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
index 41cc5d0..7424571 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
+++ b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
@@ -30,6 +30,11 @@
namespace automotive {
namespace remoteaccess {
+// The following are the same as VehicleApPowerBootupReason defined in VHAL.
+constexpr int32_t BOOTUP_REASON_USER_POWER_ON = 0;
+constexpr int32_t BOOTUP_REASON_SYSTEM_REMOTE_ACCESS = 2;
+constexpr int32_t BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE = 3;
+
// A class to generate fake task for testing. Not required for real implementation. In real
// implementation, the task should come from remote task server. This class is thread-safe.
class FakeTaskGenerator final {
@@ -98,50 +103,57 @@
};
// forward-declaration
-class TestWakeupClientServiceImpl;
+class ServiceImpl;
class TaskScheduleMsgHandler final : public android::MessageHandler {
public:
- TaskScheduleMsgHandler(TestWakeupClientServiceImpl* mImpl);
+ TaskScheduleMsgHandler(ServiceImpl* impl);
void handleMessage(const android::Message& message) override;
private:
- TestWakeupClientServiceImpl* mImpl;
+ ServiceImpl* mImpl;
};
-class TestWakeupClientServiceImpl : public WakeupClient::Service {
+class ServiceImpl {
public:
- TestWakeupClientServiceImpl();
+ ServiceImpl();
- ~TestWakeupClientServiceImpl();
+ virtual ~ServiceImpl() = 0;
// Stop the handling for all income requests. Prepare for shutdown.
void stopServer();
grpc::Status GetRemoteTasks(grpc::ServerContext* context, const GetRemoteTasksRequest* request,
- grpc::ServerWriter<GetRemoteTasksResponse>* writer) override;
+ grpc::ServerWriter<GetRemoteTasksResponse>* writer);
grpc::Status NotifyWakeupRequired(grpc::ServerContext* context,
const NotifyWakeupRequiredRequest* request,
- NotifyWakeupRequiredResponse* response) override;
+ NotifyWakeupRequiredResponse* response);
grpc::Status ScheduleTask(grpc::ServerContext* context, const ScheduleTaskRequest* request,
- ScheduleTaskResponse* response) override;
+ ScheduleTaskResponse* response);
grpc::Status UnscheduleTask(grpc::ServerContext* context, const UnscheduleTaskRequest* request,
- UnscheduleTaskResponse* response) override;
+ UnscheduleTaskResponse* response);
grpc::Status UnscheduleAllTasks(grpc::ServerContext* context,
const UnscheduleAllTasksRequest* request,
- UnscheduleAllTasksResponse* response) override;
+ UnscheduleAllTasksResponse* response);
grpc::Status IsTaskScheduled(grpc::ServerContext* context,
const IsTaskScheduledRequest* request,
- IsTaskScheduledResponse* response) override;
+ IsTaskScheduledResponse* response);
- grpc::Status GetAllPendingScheduledTasks(
- grpc::ServerContext* context, const GetAllPendingScheduledTasksRequest* request,
- GetAllPendingScheduledTasksResponse* response) override;
+ grpc::Status GetAllPendingScheduledTasks(grpc::ServerContext* context,
+ const GetAllPendingScheduledTasksRequest* request,
+ GetAllPendingScheduledTasksResponse* response);
+
+ grpc::Status IsVehicleInUse(grpc::ServerContext* context, const IsVehicleInUseRequest* request,
+ IsVehicleInUseResponse* response);
+
+ grpc::Status GetApPowerBootupReason(grpc::ServerContext* context,
+ const GetApPowerBootupReasonRequest* request,
+ GetApPowerBootupReasonResponse* response);
/**
* Starts generating fake tasks for the specific client repeatedly.
@@ -177,7 +189,7 @@
* This must be implemented by child class and contains device specific logic. E.g. this might
* be sending QEMU commands for the emulator device.
*/
- virtual void wakeupApplicationProcessor() = 0;
+ virtual void wakeupApplicationProcessor(int32_t bootupReason) = 0;
/**
* Cleans up a scheduled task info.
@@ -185,6 +197,16 @@
void cleanupScheduledTaskLocked(const std::string& clientId, const std::string& scheduleId)
REQUIRES(mLock);
+ /**
+ * Sets whether vehicle is in use.
+ */
+ void setVehicleInUse(bool vehicleInUse);
+
+ /**
+ * Sets the bootup reason.
+ */
+ void setBootupReason(int32_t bootupReason);
+
private:
friend class TaskScheduleMsgHandler;
@@ -218,6 +240,8 @@
std::atomic<bool> mServerStopped = false;
std::unordered_map<std::string, std::unordered_map<std::string, ScheduleInfo>>
mInfoByScheduleIdByClientId GUARDED_BY(mLock);
+ std::atomic<bool> mVehicleInUse = false;
+ std::atomic<int32_t> mBootupReason = BOOTUP_REASON_USER_POWER_ON;
// Thread-safe. For test impl only.
FakeTaskGenerator mFakeTaskGenerator;
@@ -232,6 +256,72 @@
void loop();
};
+class WakeupClientServiceImpl : public WakeupClient::Service {
+ public:
+ WakeupClientServiceImpl(ServiceImpl* impl) { mImpl = impl; }
+
+ grpc::Status GetRemoteTasks(grpc::ServerContext* context, const GetRemoteTasksRequest* request,
+ grpc::ServerWriter<GetRemoteTasksResponse>* writer) override {
+ return mImpl->GetRemoteTasks(context, request, writer);
+ }
+
+ grpc::Status NotifyWakeupRequired(grpc::ServerContext* context,
+ const NotifyWakeupRequiredRequest* request,
+ NotifyWakeupRequiredResponse* response) override {
+ return mImpl->NotifyWakeupRequired(context, request, response);
+ }
+
+ grpc::Status ScheduleTask(grpc::ServerContext* context, const ScheduleTaskRequest* request,
+ ScheduleTaskResponse* response) override {
+ return mImpl->ScheduleTask(context, request, response);
+ }
+
+ grpc::Status UnscheduleTask(grpc::ServerContext* context, const UnscheduleTaskRequest* request,
+ UnscheduleTaskResponse* response) override {
+ return mImpl->UnscheduleTask(context, request, response);
+ }
+
+ grpc::Status UnscheduleAllTasks(grpc::ServerContext* context,
+ const UnscheduleAllTasksRequest* request,
+ UnscheduleAllTasksResponse* response) override {
+ return mImpl->UnscheduleAllTasks(context, request, response);
+ }
+
+ grpc::Status IsTaskScheduled(grpc::ServerContext* context,
+ const IsTaskScheduledRequest* request,
+ IsTaskScheduledResponse* response) override {
+ return mImpl->IsTaskScheduled(context, request, response);
+ }
+
+ grpc::Status GetAllPendingScheduledTasks(
+ grpc::ServerContext* context, const GetAllPendingScheduledTasksRequest* request,
+ GetAllPendingScheduledTasksResponse* response) override {
+ return mImpl->GetAllPendingScheduledTasks(context, request, response);
+ }
+
+ private:
+ ServiceImpl* mImpl;
+};
+
+class PowerControllerServiceImpl : public PowerController::Service {
+ public:
+ PowerControllerServiceImpl(ServiceImpl* impl) { mImpl = impl; }
+
+ grpc::Status IsVehicleInUse(grpc::ServerContext* context, const IsVehicleInUseRequest* request,
+ IsVehicleInUseResponse* response) override {
+ return mImpl->IsVehicleInUse(context, request, response);
+ }
+
+ grpc::Status GetApPowerBootupReason(grpc::ServerContext* context,
+ const GetApPowerBootupReasonRequest* request,
+ GetApPowerBootupReasonResponse* response) override {
+ return mImpl->GetApPowerBootupReason(context, request, response);
+ }
+
+ private:
+ ServiceImpl* mImpl;
+};
+
} // namespace remoteaccess
} // namespace automotive
} // namespace hardware
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
index eed3495..5d33fcb 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
@@ -38,7 +38,7 @@
using ::grpc::Status;
constexpr int64_t kTaskIntervalInMs = 5'000;
-constexpr int64_t kTaskTimeoutInMs = 20'000;
+constexpr int64_t kTaskTimeoutInMs = 60'000;
int64_t msToNs(int64_t ms) {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(ms))
@@ -140,21 +140,21 @@
}
}
-TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {
+ServiceImpl::ServiceImpl() {
mTaskScheduleMsgHandler = android::sp<TaskScheduleMsgHandler>::make(this);
mLooper = android::sp<Looper>::make(/*opts=*/0);
mLooperThread = std::thread([this] { loop(); });
mTaskQueue = std::make_unique<TaskQueue>(mLooper);
}
-TestWakeupClientServiceImpl::~TestWakeupClientServiceImpl() {
+ServiceImpl::~ServiceImpl() {
if (mServerStopped) {
return;
}
stopServer();
}
-void TestWakeupClientServiceImpl::stopServer() {
+void ServiceImpl::stopServer() {
mTaskQueue->stopWait();
stopGeneratingFakeTask();
// Set the flag so that the loop thread will exit.
@@ -165,7 +165,7 @@
}
}
-void TestWakeupClientServiceImpl::loop() {
+void ServiceImpl::loop() {
Looper::setForThread(mLooper);
while (true) {
@@ -176,23 +176,22 @@
}
}
-void TestWakeupClientServiceImpl::injectTask(const std::string& taskData,
- const std::string& clientId) {
+void ServiceImpl::injectTask(const std::string& taskData, const std::string& clientId) {
GetRemoteTasksResponse response;
response.set_data(taskData);
response.set_clientid(clientId);
injectTaskResponse(response);
}
-void TestWakeupClientServiceImpl::injectTaskResponse(const GetRemoteTasksResponse& response) {
+void ServiceImpl::injectTaskResponse(const GetRemoteTasksResponse& response) {
printf("Receive a new task\n");
mTaskQueue->add(response);
if (mWakeupRequired) {
- wakeupApplicationProcessor();
+ wakeupApplicationProcessor(BOOTUP_REASON_SYSTEM_REMOTE_ACCESS);
}
}
-void TestWakeupClientServiceImpl::startGeneratingFakeTask(const std::string& clientId) {
+void ServiceImpl::startGeneratingFakeTask(const std::string& clientId) {
std::lock_guard<std::mutex> lockGuard(mLock);
if (mGeneratingFakeTask) {
printf("Fake task is already being generated\n");
@@ -203,7 +202,7 @@
printf("Started generating fake tasks\n");
}
-void TestWakeupClientServiceImpl::stopGeneratingFakeTask() {
+void ServiceImpl::stopGeneratingFakeTask() {
{
std::lock_guard<std::mutex> lockGuard(mLock);
if (!mGeneratingFakeTask) {
@@ -219,7 +218,7 @@
printf("Stopped generating fake tasks\n");
}
-void TestWakeupClientServiceImpl::fakeTaskGenerateLoop(const std::string& clientId) {
+void ServiceImpl::fakeTaskGenerateLoop(const std::string& clientId) {
// In actual implementation, this should communicate with the remote server and receives tasks
// from it. Here we simulate receiving one remote task every {kTaskIntervalInMs}ms.
while (true) {
@@ -237,9 +236,8 @@
}
}
-Status TestWakeupClientServiceImpl::GetRemoteTasks(ServerContext* context,
- const GetRemoteTasksRequest* request,
- ServerWriter<GetRemoteTasksResponse>* writer) {
+Status ServiceImpl::GetRemoteTasks(ServerContext* context, const GetRemoteTasksRequest* request,
+ ServerWriter<GetRemoteTasksResponse>* writer) {
printf("GetRemoteTasks called\n");
mRemoteTaskConnectionAlive = true;
while (true) {
@@ -277,15 +275,15 @@
return Status::CANCELLED;
}
-Status TestWakeupClientServiceImpl::NotifyWakeupRequired(ServerContext* context,
- const NotifyWakeupRequiredRequest* request,
- NotifyWakeupRequiredResponse* response) {
+Status ServiceImpl::NotifyWakeupRequired(ServerContext* context,
+ const NotifyWakeupRequiredRequest* request,
+ NotifyWakeupRequiredResponse* response) {
printf("NotifyWakeupRequired called\n");
if (request->iswakeuprequired() && !mWakeupRequired && !mTaskQueue->isEmpty()) {
// If wakeup is now required and previously not required, this means we have finished
// shutting down the device. If there are still pending tasks, try waking up AP again
// to finish executing those tasks.
- wakeupApplicationProcessor();
+ wakeupApplicationProcessor(BOOTUP_REASON_SYSTEM_REMOTE_ACCESS);
}
mWakeupRequired = request->iswakeuprequired();
if (mWakeupRequired) {
@@ -296,23 +294,22 @@
return Status::OK;
}
-void TestWakeupClientServiceImpl::cleanupScheduledTaskLocked(const std::string& clientId,
- const std::string& scheduleId) {
+void ServiceImpl::cleanupScheduledTaskLocked(const std::string& clientId,
+ const std::string& scheduleId) {
mInfoByScheduleIdByClientId[clientId].erase(scheduleId);
if (mInfoByScheduleIdByClientId[clientId].size() == 0) {
mInfoByScheduleIdByClientId.erase(clientId);
}
}
-TaskScheduleMsgHandler::TaskScheduleMsgHandler(TestWakeupClientServiceImpl* impl) : mImpl(impl) {}
+TaskScheduleMsgHandler::TaskScheduleMsgHandler(ServiceImpl* impl) : mImpl(impl) {}
void TaskScheduleMsgHandler::handleMessage(const android::Message& message) {
mImpl->handleAddTask(message.what);
}
-Status TestWakeupClientServiceImpl::ScheduleTask(ServerContext* context,
- const ScheduleTaskRequest* request,
- ScheduleTaskResponse* response) {
+Status ServiceImpl::ScheduleTask(ServerContext* context, const ScheduleTaskRequest* request,
+ ScheduleTaskResponse* response) {
std::lock_guard<std::mutex> lockGuard(mLock);
const GrpcScheduleInfo& grpcScheduleInfo = request->scheduleinfo();
@@ -359,8 +356,7 @@
return Status::OK;
}
-bool TestWakeupClientServiceImpl::getScheduleInfoLocked(int scheduleMsgId,
- ScheduleInfo** outScheduleInfoPtr) {
+bool ServiceImpl::getScheduleInfoLocked(int scheduleMsgId, ScheduleInfo** outScheduleInfoPtr) {
for (auto& [_, infoByScheduleId] : mInfoByScheduleIdByClientId) {
for (auto& [_, scheduleInfo] : infoByScheduleId) {
if (scheduleInfo.scheduleMsgId == scheduleMsgId) {
@@ -372,7 +368,7 @@
return false;
}
-void TestWakeupClientServiceImpl::handleAddTask(int scheduleMsgId) {
+void ServiceImpl::handleAddTask(int scheduleMsgId) {
std::lock_guard<std::mutex> lockGuard(mLock);
ScheduleInfo* scheduleInfoPtr;
@@ -385,15 +381,27 @@
const GrpcScheduleInfo& grpcScheduleInfo = *scheduleInfoPtr->grpcScheduleInfo;
const std::string scheduleId = grpcScheduleInfo.scheduleid();
const std::string clientId = grpcScheduleInfo.clientid();
-
- GetRemoteTasksResponse injectResponse;
- injectResponse.set_data(grpcScheduleInfo.data().data(), grpcScheduleInfo.data().size());
- injectResponse.set_clientid(clientId);
- injectTaskResponse(injectResponse);
scheduleInfoPtr->currentCount++;
+ ScheduleTaskType taskType = grpcScheduleInfo.tasktype();
+ printf("Sending scheduled tasks for scheduleId: %s, clientId: %s, taskCount: %d, "
+ "taskType: %d\n",
+ scheduleId.c_str(), clientId.c_str(), scheduleInfoPtr->currentCount,
+ static_cast<int>(taskType));
- printf("Sending scheduled tasks for scheduleId: %s, clientId: %s, taskCount: %d\n",
- scheduleId.c_str(), clientId.c_str(), scheduleInfoPtr->currentCount);
+ if (taskType == ScheduleTaskType::ENTER_GARAGE_MODE) {
+ if (mWakeupRequired) {
+ wakeupApplicationProcessor(BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE);
+ } else {
+ printf("Ignore ENTER_GARAGE_MODE task type because the head unit is already running");
+ }
+ } else if (grpcScheduleInfo.tasktype() == ScheduleTaskType::CUSTOM) {
+ GetRemoteTasksResponse injectResponse;
+ injectResponse.set_data(grpcScheduleInfo.data().data(), grpcScheduleInfo.data().size());
+ injectResponse.set_clientid(clientId);
+ injectTaskResponse(injectResponse);
+ } else {
+ printf("Unknown task type: %d\n", static_cast<int>(taskType));
+ }
if (scheduleInfoPtr->totalCount != 0 &&
scheduleInfoPtr->currentCount == scheduleInfoPtr->totalCount) {
@@ -407,9 +415,8 @@
android::Message(scheduleMsgId));
}
-Status TestWakeupClientServiceImpl::UnscheduleTask(ServerContext* context,
- const UnscheduleTaskRequest* request,
- UnscheduleTaskResponse* response) {
+Status ServiceImpl::UnscheduleTask(ServerContext* context, const UnscheduleTaskRequest* request,
+ UnscheduleTaskResponse* response) {
std::lock_guard<std::mutex> lockGuard(mLock);
const std::string& clientId = request->clientid();
@@ -431,9 +438,9 @@
return Status::OK;
}
-Status TestWakeupClientServiceImpl::UnscheduleAllTasks(ServerContext* context,
- const UnscheduleAllTasksRequest* request,
- UnscheduleAllTasksResponse* response) {
+Status ServiceImpl::UnscheduleAllTasks(ServerContext* context,
+ const UnscheduleAllTasksRequest* request,
+ UnscheduleAllTasksResponse* response) {
std::lock_guard<std::mutex> lockGuard(mLock);
const std::string& clientId = request->clientid();
@@ -452,9 +459,8 @@
return Status::OK;
}
-Status TestWakeupClientServiceImpl::IsTaskScheduled(ServerContext* context,
- const IsTaskScheduledRequest* request,
- IsTaskScheduledResponse* response) {
+Status ServiceImpl::IsTaskScheduled(ServerContext* context, const IsTaskScheduledRequest* request,
+ IsTaskScheduledResponse* response) {
std::lock_guard<std::mutex> lockGuard(mLock);
const std::string& clientId = request->clientid();
@@ -475,9 +481,9 @@
return Status::OK;
}
-Status TestWakeupClientServiceImpl::GetAllPendingScheduledTasks(
- ServerContext* context, const GetAllPendingScheduledTasksRequest* request,
- GetAllPendingScheduledTasksResponse* response) {
+Status ServiceImpl::GetAllPendingScheduledTasks(ServerContext* context,
+ const GetAllPendingScheduledTasksRequest* request,
+ GetAllPendingScheduledTasksResponse* response) {
const std::string& clientId = request->clientid();
printf("GetAllPendingScheduledTasks called with client Id: %s\n", clientId.c_str());
response->clear_allscheduledtasks();
@@ -493,14 +499,35 @@
return Status::OK;
}
-bool TestWakeupClientServiceImpl::isWakeupRequired() {
+Status ServiceImpl::IsVehicleInUse(ServerContext* context, const IsVehicleInUseRequest* request,
+ IsVehicleInUseResponse* response) {
+ response->set_isvehicleinuse(mVehicleInUse);
+ return Status::OK;
+}
+
+Status ServiceImpl::GetApPowerBootupReason(ServerContext* context,
+ const GetApPowerBootupReasonRequest* request,
+ GetApPowerBootupReasonResponse* response) {
+ response->set_bootupreason(mBootupReason);
+ return Status::OK;
+}
+
+bool ServiceImpl::isWakeupRequired() {
return mWakeupRequired;
}
-bool TestWakeupClientServiceImpl::isRemoteTaskConnectionAlive() {
+bool ServiceImpl::isRemoteTaskConnectionAlive() {
return mRemoteTaskConnectionAlive;
}
+void ServiceImpl::setVehicleInUse(bool vehicleInUse) {
+ mVehicleInUse = vehicleInUse;
+}
+
+void ServiceImpl::setBootupReason(int32_t bootupReason) {
+ mBootupReason = bootupReason;
+}
+
} // namespace remoteaccess
} // namespace automotive
} // namespace hardware
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
index 5443ad9..63324f3 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
@@ -33,7 +33,12 @@
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
-using ::android::hardware::automotive::remoteaccess::TestWakeupClientServiceImpl;
+using ::android::hardware::automotive::remoteaccess::BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE;
+using ::android::hardware::automotive::remoteaccess::BOOTUP_REASON_SYSTEM_REMOTE_ACCESS;
+using ::android::hardware::automotive::remoteaccess::BOOTUP_REASON_USER_POWER_ON;
+using ::android::hardware::automotive::remoteaccess::PowerControllerServiceImpl;
+using ::android::hardware::automotive::remoteaccess::ServiceImpl;
+using ::android::hardware::automotive::remoteaccess::WakeupClientServiceImpl;
using ::grpc::Server;
using ::grpc::ServerBuilder;
using ::grpc::ServerWriter;
@@ -48,11 +53,13 @@
pid_t emuPid = 0;
-void RunServer(const std::string& serviceAddr,
- std::shared_ptr<TestWakeupClientServiceImpl> service) {
+void RunServer(const std::string& serviceAddr, std::shared_ptr<ServiceImpl> service) {
ServerBuilder builder;
builder.AddListeningPort(serviceAddr, grpc::InsecureServerCredentials());
- builder.RegisterService(service.get());
+ WakeupClientServiceImpl wakeupClientService(service.get());
+ builder.RegisterService(&wakeupClientService);
+ PowerControllerServiceImpl powerControllerService(service.get());
+ builder.RegisterService(&powerControllerService);
std::unique_ptr<Server> server(builder.BuildAndStart());
printf("Test Remote Access GRPC Server listening on %s\n", serviceAddr.c_str());
server->Wait();
@@ -81,20 +88,21 @@
}
}
-bool powerOnEmu() {
+bool powerOnEmu(ServiceImpl* service, int32_t bootupReason) {
updateEmuStatus();
if (emuPid != 0) {
printf("The emulator is already running\n");
return false;
}
+ service->setBootupReason(bootupReason);
emuPid = runCommand(COMMAND_RUN_EMU);
printf("Emulator started in process: %d\n", emuPid);
return true;
}
-bool powerOn() {
+bool powerOn(ServiceImpl* service, int32_t bootupReason) {
#ifdef HOST
- return powerOnEmu();
+ return powerOnEmu(service, bootupReason);
#else
printf("power on is only supported on host\n");
return false;
@@ -133,21 +141,6 @@
#endif
}
-void setVehicleInUse(bool vehicleInUse) {
-#ifdef HOST
- printf("Set vehicleInUse to %d\n", vehicleInUse);
- int value = 0;
- if (vehicleInUse) {
- value = 1;
- }
- const char* command = getSetPropCommand(VEHICLE_IN_USE, value);
- runCommand(command);
- delete[] command;
-#else
- printf("set vehicleInUse is only supported on host\n");
-#endif
-}
-
void help() {
std::cout << "Remote Access Host Test Utility" << std::endl
<< "help:\t"
@@ -171,8 +164,7 @@
<< "(only supported on host)" << std::endl;
}
-void parseCommand(const std::string& userInput,
- std::shared_ptr<TestWakeupClientServiceImpl> service) {
+void parseCommand(const std::string& userInput, std::shared_ptr<ServiceImpl> service) {
if (userInput == "") {
// ignore empty line.
} else if (userInput == "help") {
@@ -199,8 +191,10 @@
printf("isWakeupRequired: %B, isRemoteTaskConnectionAlive: %B\n",
service->isWakeupRequired(), service->isRemoteTaskConnectionAlive());
} else if (userInput == "power on") {
- powerOn();
+ service->setVehicleInUse(true);
+ powerOn(service.get(), BOOTUP_REASON_USER_POWER_ON);
} else if (userInput == "power off") {
+ service->setVehicleInUse(false);
powerOff();
} else if (userInput.rfind("inject task", 0) == 0) {
std::stringstream ss;
@@ -226,7 +220,7 @@
printf("Remote task with client ID: %s, data: %s injected\n", clientId.c_str(),
taskData.c_str());
} else if (userInput == "set vehicleInUse") {
- setVehicleInUse(true);
+ service->setVehicleInUse(true);
} else {
printf("Unknown command, see 'help'\n");
}
@@ -242,14 +236,11 @@
exit(-1);
}
-class MyTestWakeupClientServiceImpl final : public TestWakeupClientServiceImpl {
+class MyServiceImpl final : public ServiceImpl {
public:
- void wakeupApplicationProcessor() override {
+ void wakeupApplicationProcessor(int32_t bootupReason) override {
#ifdef HOST
- if (powerOnEmu()) {
- // If we wake up AP to execute remote task, vehicle in use should be false.
- setVehicleInUse(false);
- }
+ powerOnEmu(this, bootupReason);
#else
wakeupAp();
#endif
@@ -262,8 +253,7 @@
serviceAddr = argv[1];
}
// Let the server thread run, we will force kill the server when we exit the program.
- std::shared_ptr<TestWakeupClientServiceImpl> service =
- std::make_shared<MyTestWakeupClientServiceImpl>();
+ std::shared_ptr<ServiceImpl> service = std::make_shared<MyServiceImpl>();
std::thread serverThread([serviceAddr, service] { RunServer(serviceAddr, service); });
// Register the signal handler for SIGTERM and SIGINT so that we can stop the emulator before
diff --git a/automotive/remoteaccess/test_grpc_server/impl/test/TestWakeupClientServiceImplUnitTest.cpp b/automotive/remoteaccess/test_grpc_server/impl/test/TestWakeupClientServiceImplUnitTest.cpp
index 63458ae..4bc0086 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/test/TestWakeupClientServiceImplUnitTest.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/test/TestWakeupClientServiceImplUnitTest.cpp
@@ -43,9 +43,9 @@
constexpr int64_t kTestPeriodicInSeconds = 123;
const std::string kTestGrpcAddr = "localhost:50051";
-class MyTestWakeupClientServiceImpl final : public TestWakeupClientServiceImpl {
+class MyTestWakeupClientServiceImpl final : public ServiceImpl {
public:
- void wakeupApplicationProcessor() override {
+ void wakeupApplicationProcessor([[maybe_unused]] int32_t bootupReason) override {
// Do nothing.
}
};
@@ -54,13 +54,14 @@
public:
virtual void SetUp() override {
mServerThread = std::thread([this] {
+ mService = std::make_unique<MyTestWakeupClientServiceImpl>();
+ ServerBuilder builder;
+ builder.AddListeningPort(kTestGrpcAddr, grpc::InsecureServerCredentials());
+ WakeupClientServiceImpl wakeupClientService(mService.get());
+ builder.RegisterService(&wakeupClientService);
+ mServer = builder.BuildAndStart();
{
std::unique_lock<std::mutex> lock(mLock);
- mService = std::make_unique<MyTestWakeupClientServiceImpl>();
- ServerBuilder builder;
- builder.AddListeningPort(kTestGrpcAddr, grpc::InsecureServerCredentials());
- builder.RegisterService(mService.get());
- mServer = builder.BuildAndStart();
mServerStartCv.notify_one();
}
mServer->Wait();
@@ -124,6 +125,7 @@
std::chrono::system_clock::now().time_since_epoch())
.count();
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
+ request.mutable_scheduleinfo()->set_tasktype(ScheduleTaskType::CUSTOM);
request.mutable_scheduleinfo()->set_scheduleid(scheduleId);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(count);
@@ -156,6 +158,7 @@
ScheduleTaskResponse response = {};
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
+ request.mutable_scheduleinfo()->set_tasktype(ScheduleTaskType::CUSTOM);
request.mutable_scheduleinfo()->set_scheduleid(kTestScheduleId);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(2);
@@ -191,6 +194,7 @@
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
request.mutable_scheduleinfo()->set_scheduleid(kTestScheduleId);
+ request.mutable_scheduleinfo()->set_tasktype(ScheduleTaskType::CUSTOM);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(2);
request.mutable_scheduleinfo()->set_starttimeinepochseconds(getNow() + 1);
@@ -315,6 +319,7 @@
for (int i = 0; i < 2; i++) {
EXPECT_EQ(response2.allscheduledtasks(i).clientid(), kTestClientId);
if (response2.allscheduledtasks(i).scheduleid() == scheduleId1) {
+ EXPECT_EQ(response2.allscheduledtasks(i).tasktype(), ScheduleTaskType::CUSTOM);
EXPECT_EQ(response2.allscheduledtasks(i).data(),
std::string(kTestData.begin(), kTestData.end()));
EXPECT_EQ(response2.allscheduledtasks(i).count(), count1);
@@ -322,6 +327,7 @@
EXPECT_EQ(response2.allscheduledtasks(i).periodicinseconds(), periodicInSeconds1);
} else {
EXPECT_EQ(response2.allscheduledtasks(i).scheduleid(), scheduleId2);
+ EXPECT_EQ(response2.allscheduledtasks(i).tasktype(), ScheduleTaskType::CUSTOM);
EXPECT_EQ(response2.allscheduledtasks(i).data(),
std::string(kTestData.begin(), kTestData.end()));
EXPECT_EQ(response2.allscheduledtasks(i).count(), count2);
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index e1a90cb..7306b47 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -25,9 +25,6 @@
"name": "VehiclePropertyAnnotationJavaTest"
},
{
- "name": "FakeVehicleHardwareTest"
- },
- {
"name": "FakeVehicleHalValueGeneratorsTest"
},
{
@@ -45,6 +42,9 @@
"name": "VtsHalAutomotiveVehicle_TargetTest"
},
{
+ "name": "FakeVehicleHardwareTest"
+ },
+ {
"name": "CarServiceUnitTest",
"options" : [
{
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index aab3c46..96c4a74 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -53,21 +53,25 @@
/**
* Defines if the area ID for this property is READ, WRITE or READ_WRITE. This only applies if
* the property is defined in the framework as a READ_WRITE property. Access (if set) should be
- * equal to, or a superset of, the VehiclePropConfig.access of the property.
+ * equal to, or a superset of, the VehiclePropConfig.access of the property. If access is not
+ * set for this VehicleAreaConfig (i.e. access == VehiclePropertyAccess.NONE), then it will
+ * automatically be assumed that the areaId access is the same as the VehiclePropConfig.access
+ * of the property.
*
* For example, if a property is defined as READ_WRITE, but the OEM wants to specify certain
* area Ids as READ-only, the corresponding areaIds should have an access set to READ, while the
* others must be set to READ_WRITE. We do not support setting specific area Ids to WRITE-only
* when the property is READ-WRITE.
*
- * Exclusively one of VehiclePropConfig and the VehicleAreaConfigs should be specified for a
- * single property. If VehiclePropConfig.access is populated, none of the
- * VehicleAreaConfig.access values should be populated. If VehicleAreaConfig.access values are
- * populated, VehiclePropConfig.access must not be populated.
+ * VehiclePropConfig.access should be equal the maximal subset of the accesses set in
+ * VehiclePropConfig.areaConfigs, excluding those with access == VehiclePropertyAccess.NONE. For
+ * example, if a VehiclePropConfig has some area configs with an access of
+ * VehiclePropertyAccess.READ and others with an access of VehiclePropertyAccess.READ_WRITE, the
+ * VehiclePropConfig object's access should be VehiclePropertyAccess.READ.
*
- * VehicleAreaConfigs should not be partially populated with access. If the OEM wants to specify
- * access for one area Id, all other configs should be populated with their access levels as
- * well.
+ * In the scenario where the OEM actually wants to set VehicleAreaConfig.access =
+ * VehiclePropertyAccess.NONE, the maximal subset rule should apply with this area config
+ * included, making the VehiclePropConfig.access = VehiclePropertyAccess.NONE.
*/
VehiclePropertyAccess access = VehiclePropertyAccess.NONE;
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
index 1135b26..c629b82 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
@@ -29,9 +29,20 @@
/**
* Defines if the property is read or write or both.
*
- * If populating VehicleAreaConfig.access fields for this property, this field should not be
- * populated. If the OEM decides to populate this field, none of the VehicleAreaConfig.access
- * fields should be populated.
+ * If any VehicleAreaConfig.access is not set (i.e. VehicleAreaConfig.access ==
+ * VehiclePropertyAccess.NONE) for this property, it will automatically be assumed that the
+ * areaId access is the same as the VehiclePropConfig.access.
+ *
+ * VehiclePropConfig.access should be equal the maximal subset of the accesses set in its
+ * areaConfigs, excluding those with access == VehiclePropertyAccess.NONE. For example, if a
+ * VehiclePropConfig has some area configs with an access of VehiclePropertyAccess.READ and
+ * others with an access of VehiclePropertyAccess.READ_WRITE, the VehiclePropConfig object's
+ * access should be VehiclePropertyAccess.READ.
+ *
+ * In the scenario where the OEM actually wants to set VehicleAreaConfig.access =
+ * VehiclePropertyAccess.NONE for a particular area config, the maximal subset rule should apply
+ * with this area config included, making the VehiclePropConfig.access =
+ * VehiclePropertyAccess.NONE.
*/
VehiclePropertyAccess access = VehiclePropertyAccess.NONE;
diff --git a/automotive/vehicle/aidl/emu_metadata/android.hardware.automotive.vehicle-types-meta.json b/automotive/vehicle/aidl/emu_metadata/android.hardware.automotive.vehicle-types-meta.json
index df0f51c..c812326 100644
--- a/automotive/vehicle/aidl/emu_metadata/android.hardware.automotive.vehicle-types-meta.json
+++ b/automotive/vehicle/aidl/emu_metadata/android.hardware.automotive.vehicle-types-meta.json
@@ -1089,7 +1089,7 @@
"data_enum": "TrailerState"
},
{
- "name": "Vehicle’s curb weight",
+ "name": "VEHICLE_CURB_WEIGHT",
"value": 289410886
},
{
diff --git a/automotive/vehicle/aidl/generated_lib/java/UnitsForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/UnitsForVehicleProperty.java
new file mode 100644
index 0000000..b30c8e6
--- /dev/null
+++ b/automotive/vehicle/aidl/generated_lib/java/UnitsForVehicleProperty.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * DO NOT EDIT MANUALLY!!!
+ *
+ * Generated by tools/generate_annotation_enums.py.
+ */
+
+// clang-format off
+
+package android.hardware.automotive.vehicle;
+
+import java.util.Map;
+
+public final class UnitsForVehicleProperty {
+
+ public static final Map<Integer, Integer> values = Map.ofEntries(
+ Map.entry(VehicleProperty.INFO_MODEL_YEAR, VehicleUnit.YEAR),
+ Map.entry(VehicleProperty.INFO_FUEL_CAPACITY, VehicleUnit.MILLILITER),
+ Map.entry(VehicleProperty.INFO_EV_BATTERY_CAPACITY, VehicleUnit.WATT_HOUR),
+ Map.entry(VehicleProperty.INFO_EXTERIOR_DIMENSIONS, VehicleUnit.MILLIMETER),
+ Map.entry(VehicleProperty.PERF_ODOMETER, VehicleUnit.KILOMETER),
+ Map.entry(VehicleProperty.PERF_VEHICLE_SPEED, VehicleUnit.METER_PER_SEC),
+ Map.entry(VehicleProperty.PERF_VEHICLE_SPEED_DISPLAY, VehicleUnit.METER_PER_SEC),
+ Map.entry(VehicleProperty.PERF_STEERING_ANGLE, VehicleUnit.DEGREES),
+ Map.entry(VehicleProperty.PERF_REAR_STEERING_ANGLE, VehicleUnit.DEGREES),
+ Map.entry(VehicleProperty.ENGINE_COOLANT_TEMP, VehicleUnit.CELSIUS),
+ Map.entry(VehicleProperty.ENGINE_OIL_TEMP, VehicleUnit.CELSIUS),
+ Map.entry(VehicleProperty.ENGINE_RPM, VehicleUnit.RPM),
+ Map.entry(VehicleProperty.FUEL_LEVEL, VehicleUnit.MILLILITER),
+ Map.entry(VehicleProperty.EV_BATTERY_LEVEL, VehicleUnit.WATT_HOUR),
+ Map.entry(VehicleProperty.EV_CURRENT_BATTERY_CAPACITY, VehicleUnit.WATT_HOUR),
+ Map.entry(VehicleProperty.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehicleUnit.MILLIWATTS),
+ Map.entry(VehicleProperty.RANGE_REMAINING, VehicleUnit.METER),
+ Map.entry(VehicleProperty.EV_BATTERY_AVERAGE_TEMPERATURE, VehicleUnit.CELSIUS),
+ Map.entry(VehicleProperty.TIRE_PRESSURE, VehicleUnit.KILOPASCAL),
+ Map.entry(VehicleProperty.CRITICALLY_LOW_TIRE_PRESSURE, VehicleUnit.KILOPASCAL),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_CURRENT, VehicleUnit.CELSIUS),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_SET, VehicleUnit.CELSIUS),
+ Map.entry(VehicleProperty.EXTERNAL_CAR_TIME, VehicleUnit.MILLI_SECS),
+ Map.entry(VehicleProperty.ANDROID_EPOCH_TIME, VehicleUnit.MILLI_SECS),
+ Map.entry(VehicleProperty.ENV_OUTSIDE_TEMPERATURE, VehicleUnit.CELSIUS),
+ Map.entry(VehicleProperty.WINDSHIELD_WIPERS_PERIOD, VehicleUnit.MILLI_SECS),
+ Map.entry(VehicleProperty.EV_CHARGE_CURRENT_DRAW_LIMIT, VehicleUnit.AMPERE),
+ Map.entry(VehicleProperty.EV_CHARGE_TIME_REMAINING, VehicleUnit.SECS),
+ Map.entry(VehicleProperty.CRUISE_CONTROL_TARGET_SPEED, VehicleUnit.METER_PER_SEC),
+ Map.entry(VehicleProperty.ADAPTIVE_CRUISE_CONTROL_TARGET_TIME_GAP, VehicleUnit.MILLI_SECS),
+ Map.entry(VehicleProperty.ADAPTIVE_CRUISE_CONTROL_LEAD_VEHICLE_MEASURED_DISTANCE, VehicleUnit.MILLIMETER)
+ );
+
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h
index 82e5860..00c497f 100644
--- a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h
@@ -142,10 +142,8 @@
std::vector<std::string>* errors);
// Prase a JSON field as an array of area configs.
- void parseAreas(
- const Json::Value& parentJsonNode, const std::string& fieldName,
- ConfigDeclaration* outPtr, std::vector<std::string>* errors,
- aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess defaultAccess);
+ void parseAreas(const Json::Value& parentJsonNode, const std::string& fieldName,
+ ConfigDeclaration* outPtr, std::vector<std::string>* errors);
};
} // namespace jsonconfigloader_impl
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 2a7ac96..ea1437e 100644
--- a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
@@ -68,6 +68,7 @@
using ::aidl::android::hardware::automotive::vehicle::LowSpeedCollisionWarningState;
using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
using ::aidl::android::hardware::automotive::vehicle::VehicleAirbagLocation;
+using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerBootupReason;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
@@ -303,6 +304,8 @@
std::make_unique<ConstantParser<CrossTrafficMonitoringWarningState>>();
mConstantParsersByType["LowSpeedAutomaticEmergencyBrakingState"] =
std::make_unique<ConstantParser<LowSpeedAutomaticEmergencyBrakingState>>();
+ mConstantParsersByType["VehicleApPowerBootupReason"] =
+ std::make_unique<ConstantParser<VehicleApPowerBootupReason>>();
mConstantParsersByType["Constants"] = std::make_unique<LocalVariableParser>();
#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
mConstantParsersByType["TestVendorProperty"] =
@@ -541,8 +544,7 @@
}
void JsonConfigParser::parseAreas(const Json::Value& parentJsonNode, const std::string& fieldName,
- ConfigDeclaration* config, std::vector<std::string>* errors,
- VehiclePropertyAccess defaultAccess) {
+ ConfigDeclaration* config, std::vector<std::string>* errors) {
if (!parentJsonNode.isObject()) {
errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
return;
@@ -566,8 +568,8 @@
}
VehicleAreaConfig areaConfig = {};
areaConfig.areaId = areaId;
- parseAccessChangeMode(jsonAreaConfig, "access", propStr, &defaultAccess, &areaConfig.access,
- errors);
+ parseAccessChangeMode(jsonAreaConfig, "access", propStr, &(config->config.access),
+ &areaConfig.access, errors);
tryParseJsonValueToVariable(jsonAreaConfig, "minInt32Value", /*optional=*/true,
&areaConfig.minInt32Value, errors);
tryParseJsonValueToVariable(jsonAreaConfig, "maxInt32Value", /*optional=*/true,
@@ -625,8 +627,8 @@
if (itChangeMode != ChangeModeForVehicleProperty.end()) {
defaultChangeMode = &itChangeMode->second;
}
- VehiclePropertyAccess access = VehiclePropertyAccess::NONE;
- parseAccessChangeMode(propJsonValue, "access", propStr, defaultAccessMode, &access, errors);
+ parseAccessChangeMode(propJsonValue, "access", propStr, defaultAccessMode,
+ &configDecl.config.access, errors);
parseAccessChangeMode(propJsonValue, "changeMode", propStr, defaultChangeMode,
&configDecl.config.changeMode, errors);
@@ -645,14 +647,14 @@
tryParseJsonValueToVariable(propJsonValue, "maxSampleRate", /*optional=*/true,
&configDecl.config.maxSampleRate, errors);
- parseAreas(propJsonValue, "areas", &configDecl, errors, access);
+ parseAreas(propJsonValue, "areas", &configDecl, errors);
// 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,
- .access = access,
+ .access = configDecl.config.access,
.supportVariableUpdateRate = true,
};
configDecl.config.areaConfigs.push_back(std::move(areaConfig));
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp
index a13d3df..54afbd4 100644
--- a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp
@@ -286,7 +286,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& propConfig = configs.begin()->second.config;
- ASSERT_EQ(propConfig.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(propConfig.access, VehiclePropertyAccess::READ);
ASSERT_EQ(propConfig.areaConfigs[0].access, VehiclePropertyAccess::READ);
ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::STATIC);
}
@@ -308,7 +308,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& propConfig = configs.begin()->second.config;
- ASSERT_EQ(propConfig.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(propConfig.access, VehiclePropertyAccess::WRITE);
ASSERT_EQ(propConfig.areaConfigs[0].access, VehiclePropertyAccess::WRITE);
ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::STATIC);
}
@@ -330,7 +330,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& propConfig = configs.begin()->second.config;
- ASSERT_EQ(propConfig.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(propConfig.access, VehiclePropertyAccess::READ);
ASSERT_EQ(propConfig.areaConfigs[0].access, VehiclePropertyAccess::READ);
ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::ON_CHANGE);
}
@@ -353,7 +353,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& propConfig = configs.begin()->second.config;
- ASSERT_EQ(propConfig.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(propConfig.access, VehiclePropertyAccess::WRITE);
ASSERT_EQ(propConfig.areaConfigs[0].access, VehiclePropertyAccess::WRITE);
ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::ON_CHANGE);
}
@@ -554,7 +554,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& config = configs.begin()->second.config;
- ASSERT_EQ(config.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(config.access, VehiclePropertyAccess::READ);
ASSERT_EQ(config.areaConfigs.size(), 1u);
const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
ASSERT_EQ(areaConfig.minInt32Value, 1);
@@ -641,7 +641,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& config = configs.begin()->second.config;
- ASSERT_EQ(config.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(config.access, VehiclePropertyAccess::READ);
ASSERT_EQ(config.areaConfigs.size(), 1u);
const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
@@ -670,7 +670,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& config = configs.begin()->second.config;
- ASSERT_EQ(config.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(config.access, VehiclePropertyAccess::READ);
ASSERT_EQ(config.areaConfigs.size(), 1u);
const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
@@ -702,7 +702,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& config = configs.begin()->second.config;
- ASSERT_EQ(config.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(config.access, VehiclePropertyAccess::READ);
ASSERT_EQ(config.areaConfigs.size(), 1u);
const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
@@ -731,7 +731,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& config = configs.begin()->second.config;
- ASSERT_EQ(config.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(config.access, VehiclePropertyAccess::READ_WRITE);
ASSERT_EQ(config.areaConfigs.size(), 1u);
const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
@@ -759,7 +759,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& config = configs.begin()->second.config;
- ASSERT_EQ(config.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(config.access, VehiclePropertyAccess::READ);
ASSERT_EQ(config.areaConfigs.size(), 1u);
const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
@@ -791,7 +791,7 @@
ASSERT_EQ(configs.size(), 1u);
const VehiclePropConfig& config = configs.begin()->second.config;
- ASSERT_EQ(config.access, VehiclePropertyAccess::NONE);
+ ASSERT_EQ(config.access, VehiclePropertyAccess::READ);
ASSERT_EQ(config.areaConfigs.size(), 2u);
const VehicleAreaConfig& areaConfig1 = config.areaConfigs[0];
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index b7911eb..0a859af 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -3196,6 +3196,14 @@
}
},
{
+ "property": "VehicleProperty::AP_POWER_BOOTUP_REASON",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleApPowerBootupReason::USER_POWER_ON"
+ ]
+ }
+ },
+ {
"property": "VehicleProperty::DISPLAY_BRIGHTNESS",
"defaultValue": {
"int32Values": [
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
index e75f648..5fc07c9 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
@@ -27,11 +27,16 @@
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
- cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
+ cflags: [
+ "-DENABLE_VEHICLE_HAL_TEST_PROPERTIES",
+ ],
defaults: [
"VehicleHalDefaults",
"FakeVehicleHardwareDefaults",
],
+ whole_static_libs: [
+ "wakeup_client_protos",
+ ],
}
cc_defaults {
@@ -54,7 +59,9 @@
"Prebuilt_VehicleHalVendorClusterTestProperties_JSON",
],
shared_libs: [
+ "libgrpc++",
"libjsoncpp",
+ "libprotobuf-cpp-full",
],
export_static_lib_headers: ["VehicleHalUtils"],
}
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 1153217..644d1cd 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -32,6 +32,8 @@
#include <android-base/result.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
+#include <grpc++/grpc++.h>
+#include <wakeup_client.grpc.pb.h>
#include <memory>
#include <mutex>
@@ -187,6 +189,10 @@
// Only used during initialization.
JsonConfigLoader mLoader;
+ // Only used during initialization. If not empty, points to an external grpc server that
+ // provides power controlling related properties.
+ std::string mPowerControllerServiceAddress = "";
+
void init();
// Stores the initial value to property store.
void storePropInitialValue(const ConfigDeclaration& config);
@@ -240,6 +246,11 @@
VhalResult<void> synchronizeHvacTemp(int32_t hvacDualOnAreaId,
std::optional<float> newTempC) const;
std::optional<int32_t> getSyncedAreaIdIfHvacDualOn(int32_t hvacTemperatureSetAreaId) const;
+ ValueResultType getPowerPropFromExternalService(int32_t propId) const;
+ ValueResultType getVehicleInUse(
+ android::hardware::automotive::remoteaccess::PowerController::Stub* clientStub) const;
+ ValueResultType getApPowerBootupReason(
+ android::hardware::automotive::remoteaccess::PowerController::Stub* clientStub) const;
std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations();
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 bcc765c..072aafc 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -82,6 +82,12 @@
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
+using ::android::hardware::automotive::remoteaccess::GetApPowerBootupReasonRequest;
+using ::android::hardware::automotive::remoteaccess::GetApPowerBootupReasonResponse;
+using ::android::hardware::automotive::remoteaccess::IsVehicleInUseRequest;
+using ::android::hardware::automotive::remoteaccess::IsVehicleInUseResponse;
+using ::android::hardware::automotive::remoteaccess::PowerController;
+
using ::android::base::EqualsIgnoreCase;
using ::android::base::Error;
using ::android::base::GetIntProperty;
@@ -108,6 +114,9 @@
// The directory for property configuration file that overrides the default configuration file.
// For config file format, see impl/default_config/config/README.md.
constexpr char OVERRIDE_CONFIG_DIR[] = "/vendor/etc/automotive/vhaloverride/";
+// The optional config file for power controller grpc service that provides vehicleInUse and
+// ApPowerBootupReason property.
+constexpr char GRPC_SERVICE_CONFIG_FILE[] = "/vendor/etc/automotive/powercontroller/serverconfig";
// If OVERRIDE_PROPERTY is set, we will use the configuration files from OVERRIDE_CONFIG_DIR to
// overwrite the default configs.
constexpr char OVERRIDE_PROPERTY[] = "persist.vendor.vhal_init_value_override";
@@ -256,6 +265,22 @@
},
},
};
+
+// The list of VHAL properties that might be handled by an external power controller.
+const std::unordered_set<int32_t> mPowerPropIds = {toInt(VehicleProperty::VEHICLE_IN_USE),
+ toInt(VehicleProperty::AP_POWER_BOOTUP_REASON)};
+
+void maybeGetGrpcServiceInfo(std::string* address) {
+ std::ifstream ifs(GRPC_SERVICE_CONFIG_FILE);
+ if (!ifs) {
+ ALOGI("Cannot open grpc service config file at: %s, assume no service is available",
+ GRPC_SERVICE_CONFIG_FILE);
+ return;
+ }
+ ifs >> *address;
+ ifs.close();
+}
+
} // namespace
void FakeVehicleHardware::storePropInitialValue(const ConfigDeclaration& config) {
@@ -346,6 +371,8 @@
}
void FakeVehicleHardware::init() {
+ maybeGetGrpcServiceInfo(&mPowerControllerServiceAddress);
+
for (auto& [_, configDeclaration] : loadConfigDeclarations()) {
VehiclePropConfig cfg = configDeclaration.config;
VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
@@ -763,6 +790,13 @@
int32_t propId = value.prop;
ValueResultType result;
+ if (mPowerControllerServiceAddress != "") {
+ if (mPowerPropIds.find(propId) != mPowerPropIds.end()) {
+ *isSpecialValue = true;
+ return getPowerPropFromExternalService(propId);
+ }
+ }
+
if (propId >= STARTING_VENDOR_CODE_PROPERTIES_FOR_TEST &&
propId < ENDING_VENDOR_CODE_PROPERTIES_FOR_TEST) {
*isSpecialValue = true;
@@ -844,6 +878,56 @@
return nullptr;
}
+FakeVehicleHardware::ValueResultType FakeVehicleHardware::getPowerPropFromExternalService(
+ int32_t propId) const {
+ auto channel =
+ grpc::CreateChannel(mPowerControllerServiceAddress, grpc::InsecureChannelCredentials());
+ auto clientStub = PowerController::NewStub(channel);
+ switch (propId) {
+ case toInt(VehicleProperty::VEHICLE_IN_USE):
+ return getVehicleInUse(clientStub.get());
+ case toInt(VehicleProperty::AP_POWER_BOOTUP_REASON):
+ return getApPowerBootupReason(clientStub.get());
+ default:
+ return StatusError(StatusCode::INTERNAL_ERROR)
+ << "Unsupported power property ID: " << propId;
+ }
+}
+
+FakeVehicleHardware::ValueResultType FakeVehicleHardware::getVehicleInUse(
+ PowerController::Stub* clientStub) const {
+ IsVehicleInUseRequest request = {};
+ IsVehicleInUseResponse response = {};
+ grpc::ClientContext context;
+ auto status = clientStub->IsVehicleInUse(&context, request, &response);
+ if (!status.ok()) {
+ return StatusError(StatusCode::TRY_AGAIN) << "Cannot connect to GRPC service "
+ << ", error: " << status.error_message();
+ }
+ auto result = mValuePool->obtainBoolean(response.isvehicleinuse());
+ result->prop = toInt(VehicleProperty::VEHICLE_IN_USE);
+ result->areaId = 0;
+ result->timestamp = elapsedRealtimeNano();
+ return result;
+}
+
+FakeVehicleHardware::ValueResultType FakeVehicleHardware::getApPowerBootupReason(
+ PowerController::Stub* clientStub) const {
+ GetApPowerBootupReasonRequest request = {};
+ GetApPowerBootupReasonResponse response = {};
+ grpc::ClientContext context;
+ auto status = clientStub->GetApPowerBootupReason(&context, request, &response);
+ if (!status.ok()) {
+ return StatusError(StatusCode::TRY_AGAIN) << "Cannot connect to GRPC service "
+ << ", error: " << status.error_message();
+ }
+ auto result = mValuePool->obtainInt32(response.bootupreason());
+ result->prop = toInt(VehicleProperty::AP_POWER_BOOTUP_REASON);
+ result->areaId = 0;
+ result->timestamp = elapsedRealtimeNano();
+ return result;
+}
+
FakeVehicleHardware::ValueResultType FakeVehicleHardware::getEchoReverseBytes(
const VehiclePropValue& value) const {
auto readResult = mServerSidePropStore->readValue(value);
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
index b763d2f..ac70b51 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
@@ -41,7 +41,9 @@
"libgmock",
],
shared_libs: [
+ "libgrpc++",
"libjsoncpp",
+ "libprotobuf-cpp-full",
],
data: [
":VehicleHalDefaultProperties_JSON",
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 90643aa..cab33e1 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -506,6 +506,12 @@
continue;
}
+ if (propId == toInt(VehicleProperty::VEHICLE_IN_USE) ||
+ propId == toInt(VehicleProperty::AP_POWER_BOOTUP_REASON)) {
+ // These may be controller by an external power control unit.
+ continue;
+ }
+
if (isGlobalProp(propId)) {
if (config.initialValue == RawPropValues{}) {
addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++,
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
index 1cd0d16..30f14e2 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
@@ -66,6 +66,7 @@
#include <aidl/android/hardware/automotive/vehicle/StatusCode.h>
#include <aidl/android/hardware/automotive/vehicle/SubscribeOptions.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleAirbagLocation.h>
+#include <aidl/android/hardware/automotive/vehicle/VehicleApPowerBootupReason.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReport.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReq.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleArea.h>
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
index 523cac5..aca725d 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
@@ -333,6 +333,23 @@
static_cast<aidl::android::hardware::automotive::vehicle::VehicleProperty>(propId));
}
+template <typename T>
+void roundToNearestResolution(std::vector<T>& arrayToSanitize, float resolution) {
+ if (resolution == 0) {
+ return;
+ }
+ for (size_t i = 0; i < arrayToSanitize.size(); i++) {
+ arrayToSanitize[i] = (T)((std::round(arrayToSanitize[i] / resolution)) * resolution);
+ }
+}
+
+inline void sanitizeByResolution(aidl::android::hardware::automotive::vehicle::RawPropValues* value,
+ float resolution) {
+ roundToNearestResolution(value->int32Values, resolution);
+ roundToNearestResolution(value->floatValues, resolution);
+ roundToNearestResolution(value->int64Values, resolution);
+}
+
} // 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 5053c96..2f16fca 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
@@ -25,6 +25,8 @@
#include <android-base/result.h>
#include <android-base/thread_annotations.h>
+#include <cmath>
+#include <limits>
#include <mutex>
#include <optional>
#include <unordered_map>
@@ -39,6 +41,7 @@
// A structure to represent subscription config for one subscription client.
struct SubConfig {
float sampleRateHz;
+ float resolution;
bool enableVur;
};
@@ -47,14 +50,19 @@
public:
using ClientIdType = const AIBinder*;
- void addClient(const ClientIdType& clientId, float sampleRateHz, bool enableVur);
+ void addClient(const ClientIdType& clientId, const SubConfig& subConfig);
void removeClient(const ClientIdType& clientId);
float getMaxSampleRateHz() const;
+ float getMinRequiredResolution() const;
bool isVurEnabled() const;
- bool isVurEnabledForClient(const ClientIdType& clientId);
+ bool isVurEnabledForClient(const ClientIdType& clientId) const;
+ float getResolutionForClient(const ClientIdType& clientId) const;
private:
float mMaxSampleRateHz = 0.;
+ // Baseline for resolution is maximum possible float. We want to sanitize to the highest
+ // requested resolution, which is the smallest float value for resolution.
+ float mMinRequiredResolution = std::numeric_limits<float>::max();
bool mEnableVur;
std::unordered_map<ClientIdType, SubConfig> mConfigByClient;
@@ -117,6 +125,9 @@
// Checks whether the sample rate is valid.
static bool checkSampleRateHz(float sampleRateHz);
+ // Checks whether the resolution is valid.
+ static bool checkResolution(float resolution);
+
private:
// Friend class for testing.
friend class DefaultVehicleHalTest;
@@ -153,8 +164,8 @@
VhalResult<void> addContinuousSubscriberLocked(const ClientIdType& clientId,
const PropIdAreaId& propIdAreaId,
- float sampleRateHz, bool enableVur)
- REQUIRES(mLock);
+ float sampleRateHz, float resolution,
+ 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,
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index cc5edcc..a29861f 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -722,6 +722,10 @@
return StatusError(StatusCode::INVALID_ARG)
<< "invalid sample rate: " << sampleRateHz << " HZ";
}
+ if (!SubscriptionManager::checkResolution(option.resolution)) {
+ return StatusError(StatusCode::INVALID_ARG)
+ << "invalid resolution: " << option.resolution;
+ }
}
}
return {};
diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
index 29d81a7..f1106ee 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
@@ -43,11 +43,12 @@
constexpr float ONE_SECOND_IN_NANOS = 1'000'000'000.;
SubscribeOptions newSubscribeOptions(int32_t propId, int32_t areaId, float sampleRateHz,
- bool enableVur) {
+ float resolution, bool enableVur) {
SubscribeOptions subscribedOptions;
subscribedOptions.propId = propId;
subscribedOptions.areaIds = {areaId};
subscribedOptions.sampleRate = sampleRateHz;
+ subscribedOptions.resolution = resolution;
subscribedOptions.enableVariableUpdateRate = enableVur;
return subscribedOptions;
@@ -81,8 +82,18 @@
return intervalNanos;
}
+bool SubscriptionManager::checkResolution(float resolution) {
+ if (resolution == 0) {
+ return true;
+ }
+
+ float log = std::log10(resolution);
+ return log == (int)log;
+}
+
void ContSubConfigs::refreshCombinedConfig() {
float maxSampleRateHz = 0.;
+ float minRequiredResolution = std::numeric_limits<float>::max();
bool enableVur = true;
// This is not called frequently so a brute-focre is okay. More efficient way exists but this
// is simpler.
@@ -90,6 +101,9 @@
if (subConfig.sampleRateHz > maxSampleRateHz) {
maxSampleRateHz = subConfig.sampleRateHz;
}
+ if (subConfig.resolution < minRequiredResolution) {
+ minRequiredResolution = subConfig.resolution;
+ }
if (!subConfig.enableVur) {
// If one client does not enable variable update rate, we cannot enable variable update
// rate in IVehicleHardware.
@@ -97,14 +111,12 @@
}
}
mMaxSampleRateHz = maxSampleRateHz;
+ mMinRequiredResolution = minRequiredResolution;
mEnableVur = enableVur;
}
-void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRateHz, bool enableVur) {
- mConfigByClient[clientId] = {
- .sampleRateHz = sampleRateHz,
- .enableVur = enableVur,
- };
+void ContSubConfigs::addClient(const ClientIdType& clientId, const SubConfig& subConfig) {
+ mConfigByClient[clientId] = subConfig;
refreshCombinedConfig();
}
@@ -117,12 +129,26 @@
return mMaxSampleRateHz;
}
+float ContSubConfigs::getMinRequiredResolution() const {
+ return mMinRequiredResolution;
+}
+
bool ContSubConfigs::isVurEnabled() const {
return mEnableVur;
}
-bool ContSubConfigs::isVurEnabledForClient(const ClientIdType& clientId) {
- return mConfigByClient[clientId].enableVur;
+bool ContSubConfigs::isVurEnabledForClient(const ClientIdType& clientId) const {
+ if (mConfigByClient.find(clientId) == mConfigByClient.end()) {
+ return false;
+ }
+ return mConfigByClient.at(clientId).enableVur;
+}
+
+float ContSubConfigs::getResolutionForClient(const ClientIdType& clientId) const {
+ if (mConfigByClient.find(clientId) == mConfigByClient.end()) {
+ return 0.0f;
+ }
+ return mConfigByClient.at(clientId).resolution;
}
VhalResult<void> SubscriptionManager::addOnChangeSubscriberLocked(
@@ -135,7 +161,8 @@
int32_t propId = propIdAreaId.propId;
int32_t areaId = propIdAreaId.areaId;
if (auto status = mVehicleHardware->subscribe(
- newSubscribeOptions(propId, areaId, /*updateRateHz=*/0, /*enableVur*/ false));
+ newSubscribeOptions(propId, areaId, /*updateRateHz=*/0, /*resolution*/ 0.0f,
+ /*enableVur*/ false));
status != StatusCode::OK) {
return StatusError(status)
<< StringPrintf("failed subscribe for prop: %s, areaId: %" PRId32,
@@ -146,10 +173,15 @@
VhalResult<void> SubscriptionManager::addContinuousSubscriberLocked(
const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz,
- bool enableVur) {
+ float resolution, bool enableVur) {
// Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
- newConfig.addClient(clientId, sampleRateHz, enableVur);
+ SubConfig subConfig = {
+ .sampleRateHz = sampleRateHz,
+ .resolution = resolution,
+ .enableVur = enableVur,
+ };
+ newConfig.addClient(clientId, subConfig);
return updateContSubConfigsLocked(propIdAreaId, newConfig);
}
@@ -183,7 +215,10 @@
const auto& oldConfig = mContSubConfigsByPropIdArea[propIdAreaId];
float newRateHz = newConfig.getMaxSampleRateHz();
float oldRateHz = oldConfig.getMaxSampleRateHz();
- if (newRateHz == oldRateHz && newConfig.isVurEnabled() == oldConfig.isVurEnabled()) {
+ float newResolution = newConfig.getMinRequiredResolution();
+ float oldResolution = oldConfig.getMinRequiredResolution();
+ if (newRateHz == oldRateHz && newResolution == oldResolution &&
+ newConfig.isVurEnabled() == oldConfig.isVurEnabled()) {
mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
return {};
}
@@ -199,8 +234,8 @@
}
}
if (newRateHz != 0) {
- if (auto status = mVehicleHardware->subscribe(
- newSubscribeOptions(propId, areaId, newRateHz, newConfig.isVurEnabled()));
+ if (auto status = mVehicleHardware->subscribe(newSubscribeOptions(
+ propId, areaId, newRateHz, newResolution, newConfig.isVurEnabled()));
status != StatusCode::OK) {
return StatusError(status) << StringPrintf(
"failed subscribe for prop: %s, areaId"
@@ -231,6 +266,11 @@
if (auto result = getIntervalNanos(sampleRateHz); !result.ok()) {
return StatusError(StatusCode::INVALID_ARG) << result.error().message();
}
+ if (!checkResolution(option.resolution)) {
+ return StatusError(StatusCode::INVALID_ARG) << StringPrintf(
+ "SubscribeOptions.resolution %f is not an integer power of 10",
+ option.resolution);
+ }
}
if (option.areaIds.empty()) {
@@ -253,6 +293,7 @@
VhalResult<void> result;
if (isContinuousProperty) {
result = addContinuousSubscriberLocked(clientId, propIdAreaId, option.sampleRate,
+ option.resolution,
option.enableVariableUpdateRate);
} else {
result = addOnChangeSubscriberLocked(propIdAreaId);
@@ -393,15 +434,19 @@
for (const auto& [client, callback] : mClientsByPropIdAreaId[propIdAreaId]) {
auto& subConfigs = mContSubConfigsByPropIdArea[propIdAreaId];
+ // Clients must be sent different VehiclePropValues with different levels of granularity
+ // as requested by the client using resolution.
+ VehiclePropValue newValue = value;
+ sanitizeByResolution(&(newValue.value), subConfigs.getResolutionForClient(client));
// 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);
+ if (isValueUpdatedLocked(callback, newValue)) {
+ clients[callback].push_back(newValue);
}
} else {
- clients[callback].push_back(value);
+ clients[callback].push_back(newValue);
}
}
}
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index bb82108..11a8fc7 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -234,6 +234,14 @@
},
},
{
+ .name = "invalid_resolution",
+ .option =
+ {
+ .propId = GLOBAL_CONTINUOUS_PROP,
+ .resolution = 2.0,
+ },
+ },
+ {
.name = "static_property",
.option =
{
diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
index aa5f003..f377202 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
@@ -520,6 +520,14 @@
ASSERT_FALSE(SubscriptionManager::checkSampleRateHz(0));
}
+TEST_F(SubscriptionManagerTest, testCheckResolutionValid) {
+ ASSERT_TRUE(SubscriptionManager::checkResolution(1.0));
+}
+
+TEST_F(SubscriptionManagerTest, testCheckResolutionInvalid) {
+ ASSERT_FALSE(SubscriptionManager::checkResolution(2.0));
+}
+
TEST_F(SubscriptionManagerTest, testSubscribe_enableVur) {
std::vector<SubscribeOptions> options = {{
.propId = 0,
@@ -641,6 +649,102 @@
<< "Must filter out property events if VUR is enabled";
}
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur_filterUnchangedEvents_withResolution) {
+ 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,
+ .resolution = 0.01,
+ .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,
+ .resolution = 0.1,
+ .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,
+ .resolution = 0.01,
+ // This is enabled for client2, but disabled for client1.
+ .enableVariableUpdateRate = false,
+ },
+ SubscribeOptions{
+ .propId = 0,
+ .areaIds = {1},
+ .sampleRate = 20.0,
+ .resolution = 0.1,
+ .enableVariableUpdateRate = true,
+ }));
+
+ std::vector<VehiclePropValue> propertyEvents = {{
+ .prop = 0,
+ .areaId = 0,
+ .value = {.floatValues = {1.0}},
+ .timestamp = 1,
+ },
+ {
+ .prop = 0,
+ .areaId = 1,
+ .value = {.floatValues = {1.0}},
+ .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]));
+
+ clients = getManager()->getSubscribedClients({{
+ .prop = 0,
+ .areaId = 0,
+ .value = {.floatValues = {1.01}},
+ .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 and change is too small";
+ ASSERT_TRUE(abs(clients[client1][0].value.floatValues[0] - 1.01) < 0.0000001)
+ << "Expected property value == 1.01, instead got "
+ << clients[client1][0].value.floatValues[0];
+
+ clients = getManager()->getSubscribedClients({{
+ .prop = 0,
+ .areaId = 1,
+ .value = {.floatValues = {1.06}},
+ .timestamp = 3,
+ }});
+
+ ASSERT_TRUE(clients.find(client1) == clients.end())
+ << "Must not get property events for an areaId that the client hasn't subscribed to";
+ ASSERT_FALSE(clients.find(client2) == clients.end())
+ << "Must get property events significant changes";
+ ASSERT_TRUE(abs(clients[client2][0].value.floatValues[0] - 1.1) < 0.0000001)
+ << "Expected property value == 1.1, instead got "
+ << clients[client2][0].value.floatValues[0];
+}
+
TEST_F(SubscriptionManagerTest, testSubscribe_enableVur_mustNotFilterStatusChange) {
SpAIBinder binder1 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 812b9b9..6f5c0c1 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -79,7 +79,7 @@
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:YEAR
+ * @unit VehicleUnit.YEAR
* @version 2
*/
INFO_MODEL_YEAR = 0x0103 + 0x10000000 + 0x01000000
@@ -89,7 +89,7 @@
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:MILLILITER
+ * @unit VehicleUnit.MILLILITER
* @version 2
*/
INFO_FUEL_CAPACITY = 0x0104 + 0x10000000 + 0x01000000
@@ -124,7 +124,7 @@
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:WH
+ * @unit VehicleUnit.WATT_HOUR
* @version 2
*/
INFO_EV_BATTERY_CAPACITY = 0x0106 + 0x10000000 + 0x01000000
@@ -184,7 +184,7 @@
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:MILLIMETER
+ * @unit VehicleUnit.MILLIMETER
* @version 2
*/
INFO_EXTERIOR_DIMENSIONS = 0x010B + 0x10000000 + 0x01000000
@@ -210,7 +210,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:KILOMETER
+ * @unit VehicleUnit.KILOMETER
* @version 2
*/
PERF_ODOMETER = 0x0204 + 0x10000000 + 0x01000000
@@ -226,7 +226,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:METER_PER_SEC
+ * @unit VehicleUnit.METER_PER_SEC
* @version 2
*/
PERF_VEHICLE_SPEED = 0x0207 + 0x10000000 + 0x01000000
@@ -239,7 +239,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:METER_PER_SEC
+ * @unit VehicleUnit.METER_PER_SEC
* @version 2
*/
PERF_VEHICLE_SPEED_DISPLAY = 0x0208 + 0x10000000 + 0x01000000
@@ -251,7 +251,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:DEGREES
+ * @unit VehicleUnit.DEGREES
* @version 2
*/
PERF_STEERING_ANGLE = 0x0209 + 0x10000000 + 0x01000000
@@ -263,7 +263,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:DEGREES
+ * @unit VehicleUnit.DEGREES
* @version 2
*/
PERF_REAR_STEERING_ANGLE = 0x0210 + 0x10000000 + 0x01000000
@@ -273,7 +273,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:CELSIUS
+ * @unit VehicleUnit.CELSIUS
* @version 2
*/
ENGINE_COOLANT_TEMP = 0x0301 + 0x10000000 + 0x01000000
@@ -293,7 +293,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:CELSIUS
+ * @unit VehicleUnit.CELSIUS
* @version 2
*/
ENGINE_OIL_TEMP = 0x0304 + 0x10000000 + 0x01000000
@@ -303,7 +303,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:RPM
+ * @unit VehicleUnit.RPM
* @version 2
*/
ENGINE_RPM = 0x0305 + 0x10000000 + 0x01000000
@@ -356,7 +356,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:MILLILITER
+ * @unit VehicleUnit.MILLILITER
* @version 2
*/
FUEL_LEVEL = 0x0307 + 0x10000000 + 0x01000000
@@ -383,7 +383,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:WH
+ * @unit VehicleUnit.WATT_HOUR
* @version 2
*/
EV_BATTERY_LEVEL = 0x0309 + 0x10000000 + 0x01000000
@@ -398,7 +398,7 @@
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:WH
+ * @unit VehicleUnit.WATT_HOUR
* @version 2
*/
EV_CURRENT_BATTERY_CAPACITY =
@@ -433,7 +433,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:MW
+ * @unit VehicleUnit.MILLIWATTS
* @version 2
*/
EV_BATTERY_INSTANTANEOUS_CHARGE_RATE = 0x030C + 0x10000000 + 0x01000000
@@ -452,7 +452,7 @@
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ_WRITE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:METER
+ * @unit VehicleUnit.METER
* @version 2
*/
RANGE_REMAINING = 0x0308 + 0x10000000 + 0x01000000
@@ -466,7 +466,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:CELSIUS
+ * @unit VehicleUnit.CELSIUS
* @version 3
*/
EV_BATTERY_AVERAGE_TEMPERATURE =
@@ -494,7 +494,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:KILOPASCAL
+ * @unit VehicleUnit.KILOPASCAL
* @version 2
*/
TIRE_PRESSURE = 0x0309 + 0x10000000 + 0x07000000
@@ -510,7 +510,7 @@
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:KILOPASCAL
+ * @unit VehicleUnit.KILOPASCAL
* @version 2
*/
CRITICALLY_LOW_TIRE_PRESSURE = 0x030A + 0x10000000 + 0x07000000
@@ -862,7 +862,7 @@
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:CELSIUS
+ * @unit VehicleUnit.CELSIUS
* @version 2
*/
HVAC_TEMPERATURE_CURRENT = 0x0502 + 0x10000000 + 0x05000000
@@ -894,7 +894,7 @@
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:CELSIUS
+ * @unit VehicleUnit.CELSIUS
* @version 2
*/
HVAC_TEMPERATURE_SET = 0x0503 + 0x10000000 + 0x05000000
@@ -1130,7 +1130,7 @@
* configArray[1] = FAHRENHEIT
*
* This parameter MAY be used for displaying any HVAC temperature in the system.
- * Values must be one of VehicleUnit::CELSIUS or VehicleUnit::FAHRENHEIT
+ * Values must be one of VehicleUnit.CELSIUS or VehicleUnit.FAHRENHEIT
* Note that internally, all temperatures are represented in floating point Celsius.
*
* If updating HVAC_TEMPERATURE_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS
@@ -1287,7 +1287,7 @@
*
* floatValues[0] = the requested value that an application wants to set a temperature to.
* floatValues[1] = the unit for floatValues[0]. It should be one of
- * {VehicleUnit:CELSIUS, VehicleUnit:FAHRENHEIT}.
+ * {VehicleUnit.CELSIUS, VehicleUnit.FAHRENHEIT}.
* floatValues[2] = the value OEMs suggested in CELSIUS. This value is not included
* in the request.
* floatValues[3] = the value OEMs suggested in FAHRENHEIT. This value is not included
@@ -1300,18 +1300,18 @@
* For example, when a user uses the voice assistant to set HVAC temperature to 66.2 in
* Fahrenheit.
* First, an application will set this property with the value
- * [66.2, (float)VehicleUnit:FAHRENHEIT,0,0].
+ * [66.2, (float)VehicleUnit.FAHRENHEIT,0,0].
* If OEMs suggest to set 19.0 in Celsius or 66.5 in Fahrenheit for user's request, then VHAL
* must generate a callback with property value
- * [66.2, (float)VehicleUnit:FAHRENHEIT, 19.0, 66.5]. After the voice assistant gets the
+ * [66.2, (float)VehicleUnit.FAHRENHEIT, 19.0, 66.5]. After the voice assistant gets the
* callback, it will inform the user and set HVAC temperature to the suggested value.
*
* Another example, an application receives 21 Celsius as the current temperature value by
* querying HVC_TEMPERATURE_SET. But the application wants to know what value is displayed on
* the car's UI in Fahrenheit.
- * For this, the application sets the property to [21, (float)VehicleUnit:CELSIUS, 0, 0]. If
+ * For this, the application sets the property to [21, (float)VehicleUnit.CELSIUS, 0, 0]. If
* the suggested value by the OEM for 21 Celsius is 70 Fahrenheit, then VHAL must generate a
- * callback with property value [21, (float)VehicleUnit:CELSIUS, 21.0, 70.0].
+ * callback with property value [21, (float)VehicleUnit.CELSIUS, 21.0, 70.0].
* In this case, the application can know that the value is 70.0 Fahrenheit in the car’s UI.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
@@ -1504,7 +1504,7 @@
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:MILLI_SECS
+ * @unit VehicleUnit.MILLI_SECS
* @version 2
*/
EXTERNAL_CAR_TIME = 0x0608 + 0x10000000 // VehiclePropertyGroup:SYSTEM
@@ -1534,7 +1534,7 @@
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.WRITE
- * @unit VehicleUnit:MILLI_SECS
+ * @unit VehicleUnit.MILLI_SECS
* @version 2
*/
ANDROID_EPOCH_TIME = 0x0606 + 0x10000000 + 0x01000000
@@ -1559,7 +1559,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:CELSIUS
+ * @unit VehicleUnit.CELSIUS
* @version 2
*/
ENV_OUTSIDE_TEMPERATURE = 0x0703 + 0x10000000 + 0x01000000
@@ -3212,7 +3212,7 @@
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:MILLI_SECS
+ * @unit VehicleUnit.MILLI_SECS
* @version 2
*/
WINDSHIELD_WIPERS_PERIOD =
@@ -4789,7 +4789,7 @@
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:AMPERE
+ * @unit VehicleUnit.AMPERE
* @version 2
*/
EV_CHARGE_CURRENT_DRAW_LIMIT = 0x0F3F + 0x10000000 + 0x01000000
@@ -4854,7 +4854,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:SECS
+ * @unit VehicleUnit.SECS
* @version 2
*/
EV_CHARGE_TIME_REMAINING = 0x0F43 + 0x10000000 + 0x01000000
@@ -4888,7 +4888,7 @@
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
/**
- * Vehicle’s curb weight
+ * Vehicle’s curb weight in kilograms.
*
* Returns the vehicle's curb weight in kilograms. Curb weight is
* the total weight of the vehicle with standard equipment and all
@@ -4905,10 +4905,8 @@
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:KILOGRAM
* @version 2
*/
-
VEHICLE_CURB_WEIGHT = 0x0F46 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
@@ -5567,7 +5565,7 @@
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:METER_PER_SEC
+ * @unit VehicleUnit.METER_PER_SEC
* @version 2
*/
CRUISE_CONTROL_TARGET_SPEED =
@@ -5599,7 +5597,7 @@
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:MILLI_SECS
+ * @unit VehicleUnit.MILLI_SECS
* @version 2
*/
ADAPTIVE_CRUISE_CONTROL_TARGET_TIME_GAP =
@@ -5630,7 +5628,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
- * @unit VehicleUnit:MILLIMETER
+ * @unit VehicleUnit.MILLIMETER
* @version 2
*/
ADAPTIVE_CRUISE_CONTROL_LEAD_VEHICLE_MEASURED_DISTANCE =
diff --git a/automotive/vehicle/tools/generate_annotation_enums.py b/automotive/vehicle/tools/generate_annotation_enums.py
index 93d408e..f279767 100755
--- a/automotive/vehicle/tools/generate_annotation_enums.py
+++ b/automotive/vehicle/tools/generate_annotation_enums.py
@@ -18,7 +18,8 @@
Need ANDROID_BUILD_TOP environmental variable to be set. This script will update
ChangeModeForVehicleProperty.h and AccessForVehicleProperty.h under generated_lib/cpp and
- ChangeModeForVehicleProperty.java, AccessForVehicleProperty.java, EnumForVehicleProperty.java under generated_lib/java.
+ ChangeModeForVehicleProperty.java, AccessForVehicleProperty.java, EnumForVehicleProperty.java
+ UnitsForVehicleProperty.java under generated_lib/java.
Usage:
$ python generate_annotation_enums.py
@@ -42,6 +43,8 @@
'AccessForVehicleProperty.java')
ENUM_JAVA_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/' +
'EnumForVehicleProperty.java')
+UNITS_JAVA_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/' +
+ 'UnitsForVehicleProperty.java')
VERSION_CPP_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/cpp/' +
'VersionForVehicleProperty.h')
SCRIPT_PATH = 'hardware/interfaces/automotive/vehicle/tools/generate_annotation_enums.py'
@@ -175,6 +178,15 @@
public static final Map<Integer, List<Class<?>>> values = Map.ofEntries(
"""
+UNITS_JAVA_HEADER = """package android.hardware.automotive.vehicle;
+
+import java.util.Map;
+
+public final class UnitsForVehicleProperty {
+
+ public static final Map<Integer, Integer> values = Map.ofEntries(
+"""
+
class PropertyConfig:
"""Represents one VHAL property definition in VehicleProperty.aidl."""
@@ -316,6 +328,12 @@
continue;
if not cpp:
annotation = "List.of(" + ', '.join([class_name + ".class" for class_name in config.enum_types]) + ")"
+ elif field == 'unit_type':
+ if not config.unit_type:
+ continue
+ if not cpp:
+ annotation = config.unit_type
+
elif field == 'version':
if cpp:
annotation = config.version
@@ -499,6 +517,12 @@
enum_types.setJavaFooter(JAVA_FOOTER)
generated_files.append(enum_types)
+ unit_type = GeneratedFile('unit_type')
+ unit_type.setJavaFilePath(os.path.join(android_top, UNITS_JAVA_FILE_PATH))
+ unit_type.setJavaHeader(UNITS_JAVA_HEADER)
+ unit_type.setJavaFooter(JAVA_FOOTER)
+ generated_files.append(unit_type)
+
version = GeneratedFile('version')
version.setCppFilePath(os.path.join(android_top, VERSION_CPP_FILE_PATH))
version.setCppHeader(VERSION_CPP_HEADER)
diff --git a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
index d9cd9d5..ccc0f3f 100644
--- a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
+++ b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
@@ -37,6 +37,7 @@
#include <chrono>
#include <mutex>
+#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -58,10 +59,12 @@
using ::android::base::StringPrintf;
using ::android::frameworks::automotive::vhal::ErrorCode;
using ::android::frameworks::automotive::vhal::HalPropError;
+using ::android::frameworks::automotive::vhal::IHalAreaConfig;
using ::android::frameworks::automotive::vhal::IHalPropConfig;
using ::android::frameworks::automotive::vhal::IHalPropValue;
using ::android::frameworks::automotive::vhal::ISubscriptionCallback;
using ::android::frameworks::automotive::vhal::IVhalClient;
+using ::android::frameworks::automotive::vhal::SubscribeOptionsBuilder;
using ::android::frameworks::automotive::vhal::VhalClientResult;
using ::android::hardware::getAllHalInstanceNames;
using ::android::hardware::Sanitize;
@@ -82,8 +85,8 @@
class VtsVehicleCallback final : public ISubscriptionCallback {
private:
std::mutex mLock;
- std::unordered_map<int32_t, size_t> mEventsCount GUARDED_BY(mLock);
- std::unordered_map<int32_t, std::vector<int64_t>> mEventTimestamps GUARDED_BY(mLock);
+ std::unordered_map<int32_t, std::vector<std::unique_ptr<IHalPropValue>>> mEvents
+ GUARDED_BY(mLock);
std::condition_variable mEventCond;
public:
@@ -92,8 +95,7 @@
std::lock_guard<std::mutex> lockGuard(mLock);
for (auto& value : values) {
int32_t propId = value->getPropId();
- mEventsCount[propId] += 1;
- mEventTimestamps[propId].push_back(value->getTimestamp());
+ mEvents[propId].push_back(std::move(value->clone()));
}
}
mEventCond.notify_one();
@@ -109,20 +111,37 @@
std::unique_lock<std::mutex> uniqueLock(mLock);
return mEventCond.wait_for(uniqueLock, timeout, [this, propId, expectedEvents] {
ScopedLockAssertion lockAssertion(mLock);
- return mEventsCount[propId] >= expectedEvents;
+ return mEvents[propId].size() >= expectedEvents;
});
}
- std::vector<int64_t> getEventTimestamps(int32_t propId) {
- {
- std::lock_guard<std::mutex> lockGuard(mLock);
- return mEventTimestamps[propId];
+ std::vector<std::unique_ptr<IHalPropValue>> getEvents(int32_t propId) {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ std::vector<std::unique_ptr<IHalPropValue>> events;
+ if (mEvents.find(propId) == mEvents.end()) {
+ return events;
}
+ for (const auto& eventPtr : mEvents[propId]) {
+ events.push_back(std::move(eventPtr->clone()));
+ }
+ return events;
+ }
+
+ std::vector<int64_t> getEventTimestamps(int32_t propId) {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ std::vector<int64_t> timestamps;
+ if (mEvents.find(propId) == mEvents.end()) {
+ return timestamps;
+ }
+ for (const auto& valuePtr : mEvents[propId]) {
+ timestamps.push_back(valuePtr->getTimestamp());
+ }
+ return timestamps;
}
void reset() {
std::lock_guard<std::mutex> lockGuard(mLock);
- mEventsCount.clear();
+ mEvents.clear();
}
};
@@ -136,6 +155,9 @@
public:
void verifyAccessMode(int actualAccess, int expectedAccess);
+ void verifyGlobalAccessIsMaximalAreaAccessSubset(
+ int propertyLevelAccess,
+ const std::vector<std::unique_ptr<IHalAreaConfig>>& areaConfigs) const;
void verifyProperty(VehicleProperty propId, VehiclePropertyAccess access,
VehiclePropertyChangeMode changeMode, VehiclePropertyGroup group,
VehicleArea area, VehiclePropertyType propertyType);
@@ -254,6 +276,23 @@
}
}
+TEST_P(VtsHalAutomotiveVehicleTargetTest, testPropConfigs_globalAccessIsMaximalAreaAccessSubset) {
+ if (!mVhalClient->isAidlVhal()) {
+ GTEST_SKIP() << "Skip for HIDL VHAL because HAL interface run-time version is only"
+ << "introduced for AIDL";
+ }
+
+ auto result = mVhalClient->getAllPropConfigs();
+ ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
+ << result.error().message();
+
+ const auto& configs = result.value();
+ for (size_t i = 0; i < configs.size(); i++) {
+ verifyGlobalAccessIsMaximalAreaAccessSubset(configs[i]->getAccess(),
+ configs[i]->getAreaConfigs());
+ }
+}
+
// Test get() return current value for properties.
TEST_P(VtsHalAutomotiveVehicleTargetTest, get) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::get");
@@ -524,6 +563,93 @@
<< "Expect not to get events after unsubscription";
}
+bool isVariableUpdateRateSupported(const std::unique_ptr<IHalPropConfig>& config, int32_t areaId) {
+ for (const auto& areaConfigPtr : config->getAreaConfigs()) {
+ if (areaConfigPtr->getAreaId() == areaId &&
+ areaConfigPtr->isVariableUpdateRateSupported()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Test subscribe with variable update rate enabled if supported.
+TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribe_enableVurIfSupported) {
+ ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribe_enableVurIfSupported");
+
+ int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+ if (!checkIsSupported(propId)) {
+ GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
+ }
+ if (!mVhalClient->isAidlVhal()) {
+ GTEST_SKIP() << "Variable update rate is only supported by AIDL VHAL";
+ }
+
+ auto propConfigsResult = mVhalClient->getPropConfigs({propId});
+
+ ASSERT_TRUE(propConfigsResult.ok()) << "Failed to get property config for PERF_VEHICLE_SPEED: "
+ << "error: " << propConfigsResult.error().message();
+ ASSERT_EQ(propConfigsResult.value().size(), 1u)
+ << "Expect to return 1 config for PERF_VEHICLE_SPEED";
+ auto& propConfig = propConfigsResult.value()[0];
+ float maxSampleRate = propConfig->getMaxSampleRate();
+ if (maxSampleRate < 1) {
+ GTEST_SKIP() << "Sample rate for vehicle speed < 1 times/sec, skip test since it would "
+ "take too long";
+ }
+ // PERF_VEHICLE_SPEED is a global property, so areaId is 0.
+ if (!isVariableUpdateRateSupported(propConfig, /* areaId= */ 0)) {
+ GTEST_SKIP() << "Variable update rate is not supported for PERF_VEHICLE_SPEED, "
+ << "skip testing";
+ }
+
+ auto client = mVhalClient->getSubscriptionClient(mCallback);
+ ASSERT_NE(client, nullptr) << "Failed to get subscription client";
+ SubscribeOptionsBuilder builder(propId);
+ // By default variable update rate is true.
+ builder.setSampleRate(maxSampleRate);
+ auto option = builder.build();
+
+ auto result = client->subscribe({option});
+
+ ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
+ ", error: %s",
+ propId, result.error().message().c_str());
+
+ ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, 1, std::chrono::seconds(2)))
+ << "Must get at least 1 events within 2 seconds after subscription for rate: "
+ << maxSampleRate;
+
+ // Sleep for 1 seconds to wait for more possible events to arrive.
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ client->unsubscribe({propId});
+
+ auto events = mCallback->getEvents(propId);
+ if (events.size() == 1) {
+ // We only received one event, the value is not changing so nothing to check here.
+ return;
+ }
+
+ // Sort the values by the timestamp.
+ std::map<int64_t, float> valuesByTimestamp;
+ for (size_t i = 0; i < events.size(); i++) {
+ valuesByTimestamp[events[i]->getTimestamp()] = events[i]->getFloatValues()[0];
+ }
+
+ size_t i = 0;
+ float previousValue;
+ for (const auto& [_, value] : valuesByTimestamp) {
+ if (i == 0) {
+ previousValue = value;
+ } else {
+ ASSERT_FALSE(value != previousValue) << "received duplicate value: " << value
+ << " when variable update rate is true";
+ previousValue = value;
+ }
+ }
+}
+
// Test subscribe() with an invalid property.
TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeInvalidProp) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribeInvalidProp");
@@ -586,42 +712,10 @@
}
}
-// Test that access mode is populated in exclusively one of the VehiclePropConfig or the
-// VehicleAreaConfigs. Either VehiclePropConfig.access must be populated, or all the
-// VehicleAreaConfig.access fields should be populated.
-TEST_P(VtsHalAutomotiveVehicleTargetTest, testAccessModeExclusivityAIDL) {
- if (!mVhalClient->isAidlVhal()) {
- GTEST_SKIP() << "Skip checking access mode for HIDL because the access mode field is only "
- "present for AIDL";
- }
-
- auto result = mVhalClient->getAllPropConfigs();
- ASSERT_TRUE(result.ok());
- for (const auto& cfgPtr : result.value()) {
- const IHalPropConfig& cfg = *cfgPtr;
-
- bool propAccessIsSet = (cfg.getAccess() != toInt(VehiclePropertyAccess::NONE));
- bool unsetAreaAccessExists = false;
- bool setAreaAccessExists = false;
-
- for (const auto& areaConfig : cfg.getAreaConfigs()) {
- if (areaConfig->getAccess() == toInt(VehiclePropertyAccess::NONE)) {
- unsetAreaAccessExists = true;
- } else {
- setAreaAccessExists = true;
- }
- }
-
- ASSERT_FALSE(propAccessIsSet && setAreaAccessExists) << StringPrintf(
- "Both prop and area config access is set for propertyId %d", cfg.getPropId());
- ASSERT_FALSE(!propAccessIsSet && !setAreaAccessExists) << StringPrintf(
- "Neither prop and area config access is set for propertyId %d", cfg.getPropId());
- ASSERT_FALSE(unsetAreaAccessExists && setAreaAccessExists) << StringPrintf(
- "Area access is only set in some configs for propertyId %d", cfg.getPropId());
- }
-}
-
void VtsHalAutomotiveVehicleTargetTest::verifyAccessMode(int actualAccess, int expectedAccess) {
+ if (actualAccess == toInt(VehiclePropertyAccess::NONE)) {
+ return;
+ }
if (expectedAccess == toInt(VehiclePropertyAccess::READ_WRITE)) {
ASSERT_TRUE(actualAccess == expectedAccess ||
actualAccess == toInt(VehiclePropertyAccess::READ))
@@ -633,6 +727,44 @@
"Expect to get VehiclePropertyAccess: %i, got %i", expectedAccess, actualAccess);
}
+void VtsHalAutomotiveVehicleTargetTest::verifyGlobalAccessIsMaximalAreaAccessSubset(
+ int propertyLevelAccess,
+ const std::vector<std::unique_ptr<IHalAreaConfig>>& areaConfigs) const {
+ bool readOnlyPresent = false;
+ bool writeOnlyPresent = false;
+ bool readWritePresent = false;
+ int maximalAreaAccessSubset = toInt(VehiclePropertyAccess::NONE);
+ for (size_t i = 0; i < areaConfigs.size(); i++) {
+ int access = areaConfigs[i]->getAccess();
+ switch (access) {
+ case toInt(VehiclePropertyAccess::READ):
+ readOnlyPresent = true;
+ break;
+ case toInt(VehiclePropertyAccess::WRITE):
+ writeOnlyPresent = true;
+ break;
+ case toInt(VehiclePropertyAccess::READ_WRITE):
+ readWritePresent = true;
+ break;
+ default:
+ ASSERT_EQ(access, toInt(VehiclePropertyAccess::NONE)) << StringPrintf(
+ "Area access can be NONE only if global property access is also NONE");
+ return;
+ }
+ }
+
+ if (readOnlyPresent && !writeOnlyPresent) {
+ maximalAreaAccessSubset = toInt(VehiclePropertyAccess::READ);
+ } else if (writeOnlyPresent) {
+ maximalAreaAccessSubset = toInt(VehiclePropertyAccess::WRITE);
+ } else if (readWritePresent) {
+ maximalAreaAccessSubset = toInt(VehiclePropertyAccess::READ_WRITE);
+ }
+ ASSERT_EQ(propertyLevelAccess, maximalAreaAccessSubset) << StringPrintf(
+ "Expected global access to be equal to maximal area access subset %d, Instead got %d",
+ maximalAreaAccessSubset, propertyLevelAccess);
+}
+
// Helper function to compare actual vs expected property config
void VtsHalAutomotiveVehicleTargetTest::verifyProperty(VehicleProperty propId,
VehiclePropertyAccess access,
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
index 7b98634..789e8a1 100644
--- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
@@ -2583,6 +2583,10 @@
TEST_P(BluetoothAudioProviderLeAudioOutputHardwareAidl,
GetDataPathConfiguration) {
+ if (GetProviderFactoryInterfaceVersion() <
+ BluetoothAudioHalVersion::VERSION_AIDL_V4) {
+ GTEST_SKIP();
+ }
IBluetoothAudioProvider::StreamConfig sink_requirement;
IBluetoothAudioProvider::StreamConfig source_requirement;
std::vector<IBluetoothAudioProvider::LeAudioDataPathConfiguration>
diff --git a/camera/device/aidl/android/hardware/camera/device/ICameraDevice.aidl b/camera/device/aidl/android/hardware/camera/device/ICameraDevice.aidl
index da3d5df..440c2b3 100644
--- a/camera/device/aidl/android/hardware/camera/device/ICameraDevice.aidl
+++ b/camera/device/aidl/android/hardware/camera/device/ICameraDevice.aidl
@@ -450,6 +450,9 @@
* - ANDROID_CONTROL_ZOOM_RATIO_RANGE
* - SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
*
+ * No other tags (other than vendor tags) should be set in the characteristics returned from
+ * the HAL.
+ *
* A service specific error will be returned on the following conditions
* INTERNAL_ERROR:
* The camera device cannot be opened due to an internal
diff --git a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
index 9a5f248..6e3ddc9 100644
--- a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
+++ b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
@@ -330,10 +330,14 @@
StreamConfiguration config;
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config);
- CameraMetadata chars;
- ret = device->getSessionCharacteristics(config, &chars);
+ CameraMetadata camera_chars;
+ ret = device->getCameraCharacteristics(&camera_chars);
ASSERT_TRUE(ret.isOk());
- verifySessionCharacteristics(chars);
+
+ CameraMetadata session_chars;
+ ret = device->getSessionCharacteristics(config, &session_chars);
+ ASSERT_TRUE(ret.isOk());
+ verifySessionCharacteristics(session_chars, camera_chars);
}
} else {
ALOGI("getSessionCharacteristics: Test skipped.\n");
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index ce5b849..aef50d4 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -1916,52 +1916,83 @@
}
}
-void CameraAidlTest::verifySessionCharacteristics(const CameraMetadata& chars) {
- if (flags::feature_combination_query()) {
- const camera_metadata_t* metadata =
- reinterpret_cast<const camera_metadata_t*>(chars.metadata.data());
+void CameraAidlTest::verifySessionCharacteristics(const CameraMetadata& session_chars,
+ const CameraMetadata& camera_chars) {
+ if (!flags::feature_combination_query()) {
+ return;
+ }
- size_t expectedSize = chars.metadata.size();
- int result = validate_camera_metadata_structure(metadata, &expectedSize);
- ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED));
- size_t entryCount = get_camera_metadata_entry_count(metadata);
- ASSERT_GT(entryCount, 0u);
+ const camera_metadata_t* session_metadata =
+ reinterpret_cast<const camera_metadata_t*>(session_chars.metadata.data());
- camera_metadata_ro_entry entry;
- int retcode = 0;
- float maxDigitalZoom = 1.0;
+ const camera_metadata_t* camera_metadata =
+ reinterpret_cast<const camera_metadata_t*>(camera_chars.metadata.data());
- retcode = find_camera_metadata_ro_entry(metadata, ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
- &entry);
- // ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM should always be present.
- if ((0 == retcode) && (entry.count == 1)) {
- maxDigitalZoom = entry.data.f[0];
- } else {
- ADD_FAILURE() << "Get camera scalerAvailableMaxDigitalZoom failed!";
+ size_t expectedSize = session_chars.metadata.size();
+ int result = validate_camera_metadata_structure(session_metadata, &expectedSize);
+ ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED));
+ size_t entryCount = get_camera_metadata_entry_count(session_metadata);
+ // There should be at least 1 characteristic present:
+ // SCALER_MAX_DIGITAL_ZOOM must always be available.
+ // ZOOM_RATIO_RANGE must be available if ZOOM_RATIO is supported.
+ ASSERT_TRUE(entryCount >= 1);
+
+ camera_metadata_ro_entry entry;
+ int retcode = 0;
+ float maxDigitalZoom = 1.0;
+
+ for (size_t i = 0; i < entryCount; i++) {
+ retcode = get_camera_metadata_ro_entry(session_metadata, i, &entry);
+ ASSERT_TRUE(retcode == 0);
+
+ std::set<uint32_t> allowed_tags = {ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ ANDROID_CONTROL_ZOOM_RATIO_RANGE};
+
+ if (contains(allowed_tags, entry.tag)) {
+ continue;
}
- retcode = find_camera_metadata_ro_entry(metadata, ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
- bool hasZoomRatioRange = (0 == retcode && entry.count == 2);
- if (!hasZoomRatioRange) {
- return;
- }
- float minZoomRatio = entry.data.f[0];
- float maxZoomRatio = entry.data.f[1];
- constexpr float FLOATING_POINT_THRESHOLD = 0.00001f;
- if (abs(maxDigitalZoom - maxZoomRatio) > FLOATING_POINT_THRESHOLD) {
- ADD_FAILURE() << "Difference between maximum digital zoom " << maxDigitalZoom
- << " and maximum zoom ratio " << maxZoomRatio
- << " is greater than the threshold " << FLOATING_POINT_THRESHOLD << "!";
- }
- if (minZoomRatio > maxZoomRatio) {
- ADD_FAILURE() << "Maximum zoom ratio is less than minimum zoom ratio!";
- }
- if (minZoomRatio > 1.0f) {
- ADD_FAILURE() << "Minimum zoom ratio is more than 1.0!";
- }
- if (maxZoomRatio < 1.0f) {
- ADD_FAILURE() << "Maximum zoom ratio is less than 1.0!";
- }
+ // Other than the ones above, no tags should be allowed apart from vendor tags.
+ ASSERT_TRUE(entry.tag >= VENDOR_SECTION_START);
+ }
+
+ retcode = find_camera_metadata_ro_entry(session_metadata,
+ ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, &entry);
+ if ((0 == retcode) && (entry.count == 1)) {
+ maxDigitalZoom = entry.data.f[0];
+ } else {
+ ADD_FAILURE() << "Get camera scalerAvailableMaxDigitalZoom failed!";
+ }
+
+ retcode = find_camera_metadata_ro_entry(camera_metadata, ANDROID_CONTROL_ZOOM_RATIO_RANGE,
+ &entry);
+ bool hasZoomRatioRange = (0 == retcode && entry.count == 2);
+ if (!hasZoomRatioRange) {
+ ALOGI("Skipping the rest of the test as ZOOM_RATIO_RANGE is not in camera characteristics");
+ return;
+ }
+
+ // Session characteristics must contain zoom_ratio_range if camera characteristics has it.
+ retcode = find_camera_metadata_ro_entry(session_metadata, ANDROID_CONTROL_ZOOM_RATIO_RANGE,
+ &entry);
+ ASSERT_TRUE(0 == retcode && entry.count == 2);
+
+ float minZoomRatio = entry.data.f[0];
+ float maxZoomRatio = entry.data.f[1];
+ constexpr float FLOATING_POINT_THRESHOLD = 0.00001f;
+ if (abs(maxDigitalZoom - maxZoomRatio) > FLOATING_POINT_THRESHOLD) {
+ ADD_FAILURE() << "Difference between maximum digital zoom " << maxDigitalZoom
+ << " and maximum zoom ratio " << maxZoomRatio
+ << " is greater than the threshold " << FLOATING_POINT_THRESHOLD << "!";
+ }
+ if (minZoomRatio > maxZoomRatio) {
+ ADD_FAILURE() << "Maximum zoom ratio is less than minimum zoom ratio!";
+ }
+ if (minZoomRatio > 1.0f) {
+ ADD_FAILURE() << "Minimum zoom ratio is more than 1.0!";
+ }
+ if (maxZoomRatio < 1.0f) {
+ ADD_FAILURE() << "Maximum zoom ratio is less than 1.0!";
}
}
diff --git a/camera/provider/aidl/vts/camera_aidl_test.h b/camera/provider/aidl/vts/camera_aidl_test.h
index 7bcf430..782794b 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.h
+++ b/camera/provider/aidl/vts/camera_aidl_test.h
@@ -290,7 +290,8 @@
static void verifyStreamCombination(const std::shared_ptr<ICameraDevice>& device,
const StreamConfiguration& config, bool expectedStatus);
- static void verifySessionCharacteristics(const CameraMetadata& chars);
+ static void verifySessionCharacteristics(const CameraMetadata& session_chars,
+ const CameraMetadata& camera_chars);
static void verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
const std::vector<uint8_t>& resultMetadata);
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.cpp b/gnss/1.1/vts/functional/gnss_hal_test.cpp
index 6663a19..5ec9806 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test.cpp
@@ -168,8 +168,7 @@
manager->listManifestByInterface(
"android.hardware.gnss@1.1::IGnss",
[&hasGnssHalVersion_1_1](const hidl_vec<hidl_string>& registered) {
- ASSERT_EQ(1, registered.size());
- hasGnssHalVersion_1_1 = true;
+ hasGnssHalVersion_1_1 = registered.size() != 0;
});
bool hasGnssHalVersion_2_0 = false;
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index 5e45fd9..f72cf55 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -1291,6 +1291,9 @@
frameIntervalPowerHints.cend());
EXPECT_LE(minFrameInterval->frameIntervalNs,
VtsComposerClient::kMaxFrameIntervalNs);
+ const auto maxFrameInterval = *max_element(frameIntervalPowerHints.cbegin(),
+ frameIntervalPowerHints.cend());
+ EXPECT_GE(maxFrameInterval->frameIntervalNs, vrrConfig.minFrameIntervalNs);
EXPECT_TRUE(std::all_of(frameIntervalPowerHints.cbegin(),
frameIntervalPowerHints.cend(),
@@ -1385,17 +1388,6 @@
}
}
-// TODO(b/291792736) Add detailed VTS test cases for NotifyExpectedPresent
-TEST_P(GraphicsComposerAidlV3Test, NotifyExpectedPresent) {
- for (const auto& display : mDisplays) {
- EXPECT_TRUE(mComposerClient
- ->notifyExpectedPresent(display.getDisplayId(),
- ClockMonotonicTimestamp{0},
- std::chrono::nanoseconds{8ms}.count())
- .isOk());
- }
-}
-
// Tests for Command.
class GraphicsComposerAidlCommandTest : public GraphicsComposerAidlTest {
protected:
@@ -1539,18 +1531,20 @@
}
sp<::android::Fence> presentAndGetFence(
- std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
- auto& writer = getWriter(getPrimaryDisplayId());
- writer.validateDisplay(getPrimaryDisplayId(), expectedPresentTime,
- VtsComposerClient::kNoFrameIntervalNs);
+ std::optional<ClockMonotonicTimestamp> expectedPresentTime,
+ std::optional<int64_t> displayIdOpt = {},
+ int32_t frameIntervalNs = VtsComposerClient::kNoFrameIntervalNs) {
+ const auto displayId = displayIdOpt.value_or(getPrimaryDisplayId());
+ auto& writer = getWriter(displayId);
+ writer.validateDisplay(displayId, expectedPresentTime, frameIntervalNs);
execute();
EXPECT_TRUE(mReader.takeErrors().empty());
- writer.presentDisplay(getPrimaryDisplayId());
+ writer.presentDisplay(displayId);
execute();
EXPECT_TRUE(mReader.takeErrors().empty());
- auto presentFence = mReader.takePresentFence(getPrimaryDisplayId());
+ auto presentFence = mReader.takePresentFence(displayId);
// take ownership
const int fenceOwner = presentFence.get();
*presentFence.getR() = -1;
@@ -1569,18 +1563,17 @@
return vsyncPeriod;
}
- int64_t createOnScreenLayer(Composition composition = Composition::DEVICE) {
- auto& writer = getWriter(getPrimaryDisplayId());
+ int64_t createOnScreenLayer(const VtsDisplay& display,
+ Composition composition = Composition::DEVICE) {
+ auto& writer = getWriter(display.getDisplayId());
const auto& [status, layer] =
- mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount, &writer);
+ mComposerClient->createLayer(display.getDisplayId(), kBufferSlotCount, &writer);
EXPECT_TRUE(status.isOk());
- Rect displayFrame{0, 0, getPrimaryDisplay().getDisplayWidth(),
- getPrimaryDisplay().getDisplayHeight()};
- FRect cropRect{0, 0, (float)getPrimaryDisplay().getDisplayWidth(),
- (float)getPrimaryDisplay().getDisplayHeight()};
- configureLayer(getPrimaryDisplay(), layer, composition, displayFrame, cropRect);
+ Rect displayFrame{0, 0, display.getDisplayWidth(), display.getDisplayHeight()};
+ FRect cropRect{0, 0, (float)display.getDisplayWidth(), (float)display.getDisplayHeight()};
+ configureLayer(display, layer, composition, displayFrame, cropRect);
- writer.setLayerDataspace(getPrimaryDisplayId(), layer, common::Dataspace::UNKNOWN);
+ writer.setLayerDataspace(display.getDisplayId(), layer, common::Dataspace::UNKNOWN);
return layer;
}
@@ -1692,7 +1685,7 @@
ASSERT_NE(nullptr, buffer1);
ASSERT_NE(nullptr, buffer2);
- const auto layer = createOnScreenLayer();
+ const auto layer = createOnScreenLayer(getPrimaryDisplay());
auto& writer = getWriter(getPrimaryDisplayId());
writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer1->handle,
/*acquireFence*/ -1);
@@ -1725,6 +1718,38 @@
ASSERT_TRUE(mComposerClient->setPowerMode(getPrimaryDisplayId(), PowerMode::OFF).isOk());
}
+ void forEachNotifyExpectedPresentConfig(
+ std::function<void(VtsDisplay&, const DisplayConfiguration&)> func) {
+ for (VtsDisplay& display : mDisplays) {
+ const auto displayId = display.getDisplayId();
+ EXPECT_TRUE(mComposerClient->setPowerMode(displayId, PowerMode::ON).isOk());
+ const auto& [status, displayConfigurations] =
+ mComposerClient->getDisplayConfigurations(displayId);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_FALSE(displayConfigurations.empty());
+ for (const auto& config : displayConfigurations) {
+ if (config.vrrConfig && config.vrrConfig->notifyExpectedPresentConfig) {
+ const auto [vsyncPeriodStatus, oldVsyncPeriod] =
+ mComposerClient->getDisplayVsyncPeriod(displayId);
+ ASSERT_TRUE(vsyncPeriodStatus.isOk());
+ const auto& [timelineStatus, timeline] =
+ mComposerClient->setActiveConfigWithConstraints(
+ &display, config.configId,
+ VsyncPeriodChangeConstraints{.seamlessRequired = false});
+ ASSERT_TRUE(timelineStatus.isOk());
+ if (timeline.refreshRequired) {
+ sendRefreshFrame(display, &timeline);
+ }
+ waitForVsyncPeriodChange(displayId, timeline, systemTime(), oldVsyncPeriod,
+ config.vsyncPeriod);
+ func(display, config);
+ }
+ }
+ EXPECT_TRUE(
+ mComposerClient->setPowerMode(getPrimaryDisplayId(), PowerMode::OFF).isOk());
+ }
+ }
+
void configureLayer(const VtsDisplay& display, int64_t layer, Composition composition,
const Rect& displayFrame, const FRect& cropRect) {
auto& writer = getWriter(display.getDisplayId());
@@ -2592,7 +2617,7 @@
const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
ASSERT_NE(nullptr, buffer->handle);
- const auto layer = createOnScreenLayer();
+ const auto layer = createOnScreenLayer(getPrimaryDisplay());
auto& writer = getWriter(getPrimaryDisplayId());
writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer->handle,
/*acquireFence*/ -1);
@@ -2778,8 +2803,8 @@
}
// Send the REFRESH_RATE_INDICATOR update
- ASSERT_NO_FATAL_FAILURE(
- sendBufferUpdate(createOnScreenLayer(Composition::REFRESH_RATE_INDICATOR)));
+ ASSERT_NO_FATAL_FAILURE(sendBufferUpdate(
+ createOnScreenLayer(getPrimaryDisplay(), Composition::REFRESH_RATE_INDICATOR)));
std::this_thread::sleep_for(1s);
EXPECT_FALSE(checkIfCallbackRefreshRateChangedDebugEnabledReceived(displayFilter))
<< "A callback should not be received for REFRESH_RATE_INDICATOR";
@@ -3075,6 +3100,141 @@
ASSERT_TRUE(errors.size() == 1 && errors[0].errorCode == IComposerClient::EX_BAD_LAYER);
}
+TEST_P(GraphicsComposerAidlCommandV3Test, notifyExpectedPresentTimeout) {
+ if (hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ GTEST_SUCCEED() << "Device has unreliable present fences capability, skipping";
+ return;
+ }
+ forEachNotifyExpectedPresentConfig([&](VtsDisplay& display,
+ const DisplayConfiguration& config) {
+ const auto displayId = display.getDisplayId();
+ auto minFrameIntervalNs = config.vrrConfig->minFrameIntervalNs;
+ const auto timeoutNs = config.vrrConfig->notifyExpectedPresentConfig->timeoutNs;
+
+ const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer);
+ const auto layer = createOnScreenLayer(display);
+ auto& writer = getWriter(displayId);
+ writer.setLayerBuffer(displayId, layer, /*slot*/ 0, buffer->handle,
+ /*acquireFence*/ -1);
+ sp<::android::Fence> presentFence = presentAndGetFence(ComposerClientWriter::kNoTimestamp,
+ displayId, minFrameIntervalNs);
+ presentFence->waitForever(LOG_TAG);
+ auto lastPresentTimeNs = presentFence->getSignalTime();
+
+ // Frame presents 30ms after timeout
+ const auto timeout = static_cast<const std::chrono::nanoseconds>(timeoutNs);
+ const auto vsyncPeriod = config.vsyncPeriod;
+ int32_t frameAfterTimeoutNs =
+ vsyncPeriod * static_cast<int32_t>((timeout + 30ms).count() / vsyncPeriod);
+ auto expectedPresentTimestamp =
+ ClockMonotonicTimestamp{lastPresentTimeNs + frameAfterTimeoutNs};
+ std::this_thread::sleep_for(timeout);
+ mComposerClient->notifyExpectedPresent(displayId, expectedPresentTimestamp,
+ minFrameIntervalNs);
+ presentFence = presentAndGetFence(expectedPresentTimestamp, displayId, minFrameIntervalNs);
+ presentFence->waitForever(LOG_TAG);
+ lastPresentTimeNs = presentFence->getSignalTime();
+ ASSERT_GE(lastPresentTimeNs, expectedPresentTimestamp.timestampNanos - vsyncPeriod / 2);
+ mComposerClient->destroyLayer(displayId, layer, &writer);
+ });
+}
+
+TEST_P(GraphicsComposerAidlCommandV3Test, notifyExpectedPresentFrameIntervalChange) {
+ if (hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ GTEST_SUCCEED() << "Device has unreliable present fences capability, skipping";
+ return;
+ }
+ forEachNotifyExpectedPresentConfig([&](VtsDisplay& display,
+ const DisplayConfiguration& config) {
+ const auto displayId = display.getDisplayId();
+ const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer);
+ const auto layer = createOnScreenLayer(display);
+ auto& writer = getWriter(displayId);
+ writer.setLayerBuffer(displayId, layer, /*slot*/ 0, buffer->handle,
+ /*acquireFence*/ -1);
+ auto minFrameIntervalNs = config.vrrConfig->minFrameIntervalNs;
+ sp<::android::Fence> presentFence = presentAndGetFence(ComposerClientWriter::kNoTimestamp,
+ displayId, minFrameIntervalNs);
+ presentFence->waitForever(LOG_TAG);
+ auto lastPresentTimeNs = presentFence->getSignalTime();
+
+ auto vsyncPeriod = config.vsyncPeriod;
+ int32_t highestDivisor = VtsComposerClient::kMaxFrameIntervalNs / vsyncPeriod;
+ int32_t lowestDivisor = minFrameIntervalNs / vsyncPeriod;
+ const auto headsUpNs = config.vrrConfig->notifyExpectedPresentConfig->headsUpNs;
+ float totalDivisorsPassed = 0.f;
+ for (int divisor = lowestDivisor; divisor <= highestDivisor; divisor++) {
+ const auto frameIntervalNs = vsyncPeriod * divisor;
+ const auto frameAfterHeadsUp = frameIntervalNs * (headsUpNs / frameIntervalNs);
+ auto presentTime = lastPresentTimeNs + frameIntervalNs + frameAfterHeadsUp;
+ const auto expectedPresentTimestamp = ClockMonotonicTimestamp{presentTime};
+ ASSERT_TRUE(mComposerClient
+ ->notifyExpectedPresent(displayId, expectedPresentTimestamp,
+ frameIntervalNs)
+ .isOk());
+ presentFence = presentAndGetFence(expectedPresentTimestamp, displayId, frameIntervalNs);
+ presentFence->waitForever(LOG_TAG);
+ lastPresentTimeNs = presentFence->getSignalTime();
+ if (lastPresentTimeNs >= expectedPresentTimestamp.timestampNanos - vsyncPeriod / 2) {
+ ++totalDivisorsPassed;
+ }
+ }
+ EXPECT_TRUE(totalDivisorsPassed >
+ (static_cast<float>(highestDivisor - lowestDivisor)) * 0.75f);
+ mComposerClient->destroyLayer(displayId, layer, &writer);
+ });
+}
+
+TEST_P(GraphicsComposerAidlCommandV3Test, frameIntervalChangeAtPresentFrame) {
+ if (hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ GTEST_SUCCEED() << "Device has unreliable present fences capability, skipping";
+ return;
+ }
+ forEachNotifyExpectedPresentConfig([&](VtsDisplay& display,
+ const DisplayConfiguration& config) {
+ const auto displayId = display.getDisplayId();
+ const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer);
+ const auto layer = createOnScreenLayer(display);
+ auto& writer = getWriter(displayId);
+ writer.setLayerBuffer(displayId, layer, /*slot*/ 0, buffer->handle,
+ /*acquireFence*/ -1);
+ auto minFrameIntervalNs = config.vrrConfig->minFrameIntervalNs;
+
+ auto vsyncPeriod = config.vsyncPeriod;
+ int32_t highestDivisor = VtsComposerClient::kMaxFrameIntervalNs / vsyncPeriod;
+ int32_t lowestDivisor = minFrameIntervalNs / vsyncPeriod;
+ const auto headsUpNs = config.vrrConfig->notifyExpectedPresentConfig->headsUpNs;
+ float totalDivisorsPassed = 0.f;
+ int divisor = lowestDivisor;
+ auto frameIntervalNs = vsyncPeriod * divisor;
+ sp<::android::Fence> presentFence =
+ presentAndGetFence(ComposerClientWriter::kNoTimestamp, displayId, frameIntervalNs);
+ presentFence->waitForever(LOG_TAG);
+ auto lastPresentTimeNs = presentFence->getSignalTime();
+ do {
+ frameIntervalNs = vsyncPeriod * divisor;
+ ++divisor;
+ const auto nextFrameIntervalNs = vsyncPeriod * divisor;
+ const auto frameAfterHeadsUp = frameIntervalNs * (headsUpNs / frameIntervalNs);
+ auto presentTime = lastPresentTimeNs + frameIntervalNs + frameAfterHeadsUp;
+ const auto expectedPresentTimestamp = ClockMonotonicTimestamp{presentTime};
+ presentFence =
+ presentAndGetFence(expectedPresentTimestamp, displayId, nextFrameIntervalNs);
+ presentFence->waitForever(LOG_TAG);
+ lastPresentTimeNs = presentFence->getSignalTime();
+ if (lastPresentTimeNs >= expectedPresentTimestamp.timestampNanos - vsyncPeriod / 2) {
+ ++totalDivisorsPassed;
+ }
+ } while (divisor < highestDivisor);
+ EXPECT_TRUE(totalDivisorsPassed >
+ (static_cast<float>(highestDivisor - lowestDivisor)) * 0.75f);
+ mComposerClient->destroyLayer(displayId, layer, &writer);
+ });
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerAidlCommandTest);
INSTANTIATE_TEST_SUITE_P(
PerInstance, GraphicsComposerAidlCommandTest,
diff --git a/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp b/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
index 4ee14e3..edc25b8 100644
--- a/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
+++ b/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
@@ -65,10 +65,10 @@
template <typename T, typename S, typename R>
void testMatching(std::vector<T> const& c1, R T::*f1, std::vector<S> const& c2, R S::*f2);
- bool containsTimedEntity(const std::string& str);
+ bool isEntitySkipped(const std::string& str);
- void excludeTimedEntities(std::vector<PowerEntity>* entities,
- std::vector<StateResidencyResult>* results);
+ void excludeSkippedEntities(std::vector<PowerEntity>* entities,
+ std::vector<StateResidencyResult>* results);
std::shared_ptr<IPowerStats> powerstats;
};
@@ -116,15 +116,20 @@
EXPECT_EQ(c1fields, c2fields);
}
-bool PowerStatsAidl::containsTimedEntity(const std::string& str) {
+bool PowerStatsAidl::isEntitySkipped(const std::string& str) {
+ bool skip = false;
// TODO(b/229698505): Extend PowerEntityInfo to identify timed power entity
- return str.find("AoC") != std::string::npos;
+ skip |= str.find("AoC") != std::string::npos;
+ // Lassen GNSS power stats will be present after running GPS session once.
+ // Otherwise, VTS will fail due to missing GPS power stats.
+ skip |= str.find("GPS") != std::string::npos;
+ return skip;
}
-void PowerStatsAidl::excludeTimedEntities(std::vector<PowerEntity>* entities,
- std::vector<StateResidencyResult>* results) {
+void PowerStatsAidl::excludeSkippedEntities(std::vector<PowerEntity>* entities,
+ std::vector<StateResidencyResult>* results) {
for (auto it = entities->begin(); it != entities->end(); it++) {
- if (containsTimedEntity((*it).name)) {
+ if (isEntitySkipped((*it).name)) {
auto entityId = (*it).id;
entities->erase(it--);
@@ -214,19 +219,19 @@
}
// State residency must return all results except timed power entities
-TEST_P(PowerStatsAidl, TestGetStateResidencyAllResultsExceptTimedEntities) {
+TEST_P(PowerStatsAidl, TestGetStateResidencyAllResultsExceptSkippedEntities) {
std::vector<PowerEntity> entities;
ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
std::vector<StateResidencyResult> results;
ASSERT_OK(powerstats->getStateResidency({}, &results));
- excludeTimedEntities(&entities, &results);
+ excludeSkippedEntities(&entities, &results);
testMatching(entities, &PowerEntity::id, results, &StateResidencyResult::id);
}
// Each result must contain all state residencies except timed power entities
-TEST_P(PowerStatsAidl, TestGetStateResidencyAllStateResidenciesExceptTimedEntities) {
+TEST_P(PowerStatsAidl, TestGetStateResidencyAllStateResidenciesExceptSkippedEntities) {
std::vector<PowerEntity> entities;
ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
@@ -234,7 +239,7 @@
ASSERT_OK(powerstats->getStateResidency({}, &results));
for (auto entity : entities) {
- if (!containsTimedEntity(entity.name)) {
+ if (!isEntitySkipped(entity.name)) {
auto it = std::find_if(results.begin(), results.end(),
[&entity](const auto& x) { return x.id == entity.id; });
ASSERT_NE(it, results.end());
@@ -255,7 +260,7 @@
std::vector<PowerEntity> selectedEntities = getRandomSubset(entities);
std::vector<int32_t> selectedIds;
for (auto it = selectedEntities.begin(); it != selectedEntities.end(); it++) {
- if (!containsTimedEntity((*it).name)) {
+ if (!isEntitySkipped((*it).name)) {
selectedIds.push_back((*it).id);
} else {
selectedEntities.erase(it--);
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index aeb0163..4ebafee 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -794,33 +794,40 @@
in @nullable HardwareAuthToken authToken);
/**
- * Called by client to notify the IKeyMintDevice that the device is now locked, and keys with
- * the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable. When this function is called,
- * the IKeyMintDevice should note the current timestamp, and attempts to use
- * UNLOCKED_DEVICE_REQUIRED keys must be rejected with Error::DEVICE_LOCKED until an
- * authentication token with a later timestamp is presented. If the `passwordOnly' argument is
- * set to true the sufficiently-recent authentication token must indicate that the user
- * authenticated with a password, not a biometric.
+ * This method is deprecated and has never been used. Implementations should return
+ * ErrorCode::UNIMPLEMENTED.
*
- * Note that the IKeyMintDevice UNLOCKED_DEVICE_REQUIRED semantics are slightly different from
- * the UNLOCKED_DEVICE_REQUIRED semantics enforced by keystore. Keystore handles device locking
- * on a per-user basis. Because auth tokens do not contain an Android user ID, it's not
- * possible to replicate the keystore enforcement logic in IKeyMintDevice. So from the
- * IKeyMintDevice perspective, any user unlock unlocks all UNLOCKED_DEVICE_REQUIRED keys.
- * Keystore will continue enforcing the per-user device locking.
+ * This method was originally intended to be used to notify KeyMint that the device is now
+ * locked, and keys with the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable until a
+ * later valid HardwareAuthToken is presented. However, Android has never called this method
+ * and it cannot start doing so, because KeyMint's enforcement of UNLOCKED_DEVICE_REQUIRED did
+ * not provide the correct semantics and therefore could never be enabled. Specifically, the
+ * following issues existed with the design of KeyMint's enforcement of
+ * UNLOCKED_DEVICE_REQUIRED:
*
- * @param passwordOnly specifies whether the device must be unlocked with a password, rather
- * than a biometric, before UNLOCKED_DEVICE_REQUIRED keys can be used.
+ * o It assumed a global device lock state only. Android actually has a separate lock state for
+ * each user. See the javadoc for KeyguardManager#isDeviceLocked().
+ * o It assumed that unlocking the device involves a successful user authentication that
+ * generates a HardwareAuthToken. This is not necessarily the case, since Android supports
+ * weaker unlock methods including class 1 and 2 biometrics and trust agents. These unlock
+ * methods do not generate a HardwareAuthToken or interact with KeyMint in any way. Also,
+ * UNLOCKED_DEVICE_REQUIRED must work even for users who do not have a secure lock screen.
+ * o It would have made UNLOCKED_DEVICE_REQUIRED incompatible with requiring user
+ * authentication in some cases. These two key protections can each require a different
+ * HardwareAuthToken, but KeyMint only supports one HardwareAuthToken per operation.
+ * o It would have provided no security benefit over Keystore's enforcement of
+ * UNLOCKED_DEVICE_REQUIRED. This is because since Android 12, Keystore enforces
+ * UNLOCKED_DEVICE_REQUIRED not just logically, but it also cryptographically by
+ * superencrypting all such keys and wiping or re-encrypting the superencryption key when the
+ * device is locked (whenever possible). KeyMint is still used to support biometric unlocks,
+ * but this mechanism does not use KeyMint's direct enforcement of UNLOCKED_DEVICE_REQUIRED.
*
- * @param timestampToken is used by StrongBox implementations of IKeyMintDevice. It
- * provides the StrongBox IKeyMintDevice with a fresh, MACed timestamp which it can use as the
- * device-lock time, for future comparison against auth tokens when operations using
- * UNLOCKED_DEVICE_REQUIRED keys are attempted. Unless the auth token timestamp is newer than
- * the timestamp in the timestampToken, the device is still considered to be locked.
- * Crucially, if a StrongBox IKeyMintDevice receives a deviceLocked() call with a timestampToken
- * timestamp that is less than the timestamp in the last deviceLocked() call, it must ignore the
- * new timestamp. TEE IKeyMintDevice implementations will receive an empty timestampToken (zero
- * values and empty vectors) and should use their own clock as the device-lock time.
+ * Therefore, this method is not useful, and there is no reason for it be called.
+ * Implementations should return ErrorCode::UNIMPLEMENTED and should not include
+ * UNLOCKED_DEVICE_REQUIRED in the list of hardware-enforced key parameters.
+ *
+ * @param passwordOnly N/A due to the deprecation
+ * @param timestampToken N/A due to the deprecation
*/
void deviceLocked(in boolean passwordOnly, in @nullable TimeStampToken timestampToken);
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index be29f59..996e4e3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -482,11 +482,12 @@
/**
* Tag::UNLOCKED_DEVICE_REQUIRED specifies that the key may only be used when the device is
- * unlocked, as reported to KeyMint via authToken operation parameter and the
- * IKeyMintDevice::deviceLocked() method
+ * unlocked.
*
- * Must be hardware-enforced (but is also keystore-enforced on a per-user basis: see the
- * deviceLocked() documentation).
+ * This tag was originally intended to be hardware-enforced. However, the support for hardware
+ * enforcement of this tag is now considered deprecated because it cannot work correctly, and
+ * even if implemented it does nothing because it was never enabled by Keystore. Refer to the
+ * documentation for the deprecated method IKeyMintDevice::deviceLocked().
*/
UNLOCKED_DEVICE_REQUIRED = TagType.BOOL | 509,
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 0b7627c..a8f41c3 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -8760,40 +8760,6 @@
INSTANTIATE_KEYMINT_AIDL_TEST(EarlyBootKeyTest);
-using UnlockedDeviceRequiredTest = KeyMintAidlTestBase;
-
-// This may be a problematic test. It can't be run repeatedly without unlocking the device in
-// between runs... and on most test devices there are no enrolled credentials so it can't be
-// unlocked at all, meaning the only way to get the test to pass again on a properly-functioning
-// device is to reboot it. For that reason, this is disabled by default. It can be used as part of
-// a manual test process, which includes unlocking between runs, which is why it's included here.
-// Well, that and the fact that it's the only test we can do without also making calls into the
-// Gatekeeper HAL. We haven't written any cross-HAL tests, and don't know what all of the
-// implications might be, so that may or may not be a solution.
-TEST_P(UnlockedDeviceRequiredTest, DISABLED_KeysBecomeUnusable) {
- auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
- CreateTestKeys(TAG_UNLOCKED_DEVICE_REQUIRED, ErrorCode::OK);
- KeyBlobDeleter aes_deleter(keymint_, aesKeyData.blob);
- KeyBlobDeleter hmac_deleter(keymint_, hmacKeyData.blob);
- KeyBlobDeleter rsa_deleter(keymint_, rsaKeyData.blob);
- KeyBlobDeleter ecdsa_deleter(keymint_, ecdsaKeyData.blob);
-
- EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
- EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
- EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
- EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
-
- ErrorCode rc = GetReturnErrorCode(
- keyMint().deviceLocked(false /* passwordOnly */, {} /* timestampToken */));
- ASSERT_EQ(ErrorCode::OK, rc);
- EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseAesKey(aesKeyData.blob));
- EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseHmacKey(hmacKeyData.blob));
- EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseRsaKey(rsaKeyData.blob));
- EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseEcdsaKey(ecdsaKeyData.blob));
-}
-
-INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest);
-
using VsrRequirementTest = KeyMintAidlTestBase;
// @VsrTest = VSR-3.10-008
diff --git a/staging/security/see/storage/aidl/Android.bp b/staging/security/see/storage/aidl/Android.bp
new file mode 100644
index 0000000..f669be8
--- /dev/null
+++ b/staging/security/see/storage/aidl/Android.bp
@@ -0,0 +1,26 @@
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.security.see.storage",
+ unstable: true,
+ host_supported: true,
+ srcs: [
+ "android/hardware/security/see/storage/*.aidl",
+ ],
+ backend: {
+ java: {
+ enabled: false,
+ },
+ cpp: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl
new file mode 100644
index 0000000..1c65038
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+enum CreationMode {
+ /** Returns an error if the file does not already exist. */
+ NO_CREATE,
+
+ /** Creates the file or returns an error if it already exists. */
+ CREATE_EXCLUSIVE,
+
+ /** Creates the file if it does not already exist. */
+ CREATE,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl
new file mode 100644
index 0000000..1a94eb2
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable DeleteOptions {
+ /**
+ * Set to acknowledge possible files tampering.
+ *
+ * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+ * service-specific code.
+ */
+ ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+ /**
+ * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+ *
+ * If the A/B update fails, the operation will be rolled back. This rollback will not
+ * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+ * acknowledged by setting the `readIntegrity`.
+ */
+ boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl
new file mode 100644
index 0000000..d339170
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+/** Determines how early during the boot process file is able to be accessed. */
+enum FileAvailability {
+ /** Available before userdata is mounted, but after android has booted. */
+ BEFORE_USERDATA,
+
+ /** Available after userdata is mounted. */
+ AFTER_USERDATA,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl
new file mode 100644
index 0000000..1879b16
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+enum FileIntegrity {
+ /** REE may prevent operations, but cannot alter data once written. */
+ TAMPER_PROOF_AT_REST,
+
+ /**
+ * REE may alter written data, but changes will be detected and reported as
+ * an error on read.
+ */
+ TAMPER_DETECT,
+
+ /**
+ * REE may alter written data. Changes other than full filesystem resets will be detected and
+ * reported.
+ */
+ TAMPER_DETECT_IGNORE_RESET,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl
new file mode 100644
index 0000000..18a2eae
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+enum FileMode {
+ /** The file may only be read from. */
+ READ_ONLY,
+
+ /** The file may only be written to. */
+ WRITE_ONLY,
+
+ /** The file may be both read from and written to. */
+ READ_WRITE,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl
new file mode 100644
index 0000000..733b5b0
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.FileAvailability;
+import android.hardware.security.see.storage.FileIntegrity;
+
+parcelable FileProperties {
+ FileIntegrity integrity = FileIntegrity.TAMPER_PROOF_AT_REST;
+ FileAvailability availability = FileAvailability.BEFORE_USERDATA;
+
+ /** Whether the file is reset when user data is wiped. */
+ boolean persistent;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl
new file mode 100644
index 0000000..a0a9f3d
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+/** The interface for an open directory */
+interface IDir {
+ /**
+ * Gets the next batch of filenames in this directory.
+ *
+ * Calling multiple times will return different results as the IDir iterates through all the
+ * files it contains. When all filenames have been returned, all successive calls will return an
+ * empty list.
+ *
+ * @maxCount:
+ * the maximum number of filenames to return. A @maxCount of 0 signifies no limit on the
+ * number of filenames returned.
+ *
+ * Returns:
+ * An ordered list of filenames. If @maxCount > 0, the length of the returned list will be
+ * less than or equal to @maxCount.
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * dir was opened with does not acknowledge
+ */
+ @utf8InCpp String[] readNextFilenames(int maxCount);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl
new file mode 100644
index 0000000..ff26aa4
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+
+/** The interface for an open file */
+interface IFile {
+ /**
+ * Read bytes from this file.
+ *
+ * @size:
+ * the size (in bytes) of the segment to read. If @size is larger than the service's maximum
+ * read size, the call will return an error (EX_ILLEGAL_ARGUMENT).
+ * @offset:
+ * the offset (in bytes) at which to start reading
+ *
+ * Return:
+ * the sequence of bytes at [offset, offset + size) in the file
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ byte[] read(long size, long offset);
+
+ /**
+ * Write the bytes in `buffer` to this file.
+ *
+ * @offset:
+ * the offset (in bytes) at which to start writing
+ *
+ * Return:
+ * the number of bytes written successfully
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ long write(long offset, in byte[] buffer);
+
+ /**
+ * Reads this file's size.
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ long getSize();
+
+ /**
+ * Sets this file's size.
+ *
+ * Truncates the file if `new_size` is less than the current size. If `new_size` is greater than
+ * the current size, the file will be extended with zeroed data.
+ *
+ * @newSize:
+ * the file's new size
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ void setSize(long newSize);
+
+ /**
+ * Renames this file.
+ *
+ * @destPath:
+ * the file's new path, relative to filesystem root
+ * @destCreateMode:
+ * controls creation behavior of the dest file
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND if no file exists at @destPath and @destCreateMode is `NO_CREATE`
+ * - ERR_ALREADY_EXISTS if a file already exists at @destPath and @destCreateMode is
+ * `CREATE_EXCLUSIVE`
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ void rename(in @utf8InCpp String destPath, in CreationMode destCreateMode);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl
new file mode 100644
index 0000000..be3c045
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.FileProperties;
+import android.hardware.security.see.storage.IStorageSession;
+
+/**
+ * Interface for the Secure Storage HAL
+ *
+ * Creates sessions which can be used to access storage.
+ */
+interface ISecureStorage {
+ const int ERR_UNSUPPORTED_PROPERTIES = 1;
+ const int ERR_NOT_FOUND = 2;
+ const int ERR_ALREADY_EXISTS = 3;
+ const int ERR_BAD_TRANSACTION = 4;
+
+ const int ERR_FS_RESET = 5;
+ const int ERR_FS_ROLLED_BACK = 6;
+ const int ERR_FS_TAMPERED = 7;
+
+ /**
+ * Starts a storage session for a filesystem.
+ *
+ * @properties:
+ * the minimum filesystem properties requested for the session.
+ *
+ * May return service-specific errors:
+ * - ERR_UNSUPPORTED_PROPERTIES if no filesystems exist which meet the minimum requested
+ * requirements
+ */
+ IStorageSession startSession(in FileProperties properties);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl
new file mode 100644
index 0000000..cd126b8
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.DeleteOptions;
+import android.hardware.security.see.storage.IDir;
+import android.hardware.security.see.storage.IFile;
+import android.hardware.security.see.storage.OpenOptions;
+import android.hardware.security.see.storage.ReadIntegrity;
+import android.hardware.security.see.storage.RenameOptions;
+
+/**
+ * Interface for a Secure Storage session
+ *
+ * When the connection is opened, it will start a transaction and any changes made through this
+ * session or the interfaces this session returns will be added to this transaction's pending
+ * changes. Calling `CommitChanges`/`AbandonChanges` will commit/abandon these pending changes, and
+ * start a new, empty transaction. The interfaces this session returns _remain_ valid across
+ * transactions; it is not necessary, for example, to reopen a file after a commit.
+ *
+ * Any changes still pending when the session is dropped will be abandoned.
+ */
+interface IStorageSession {
+ /**
+ * Commits any pending changes made through this session to storage.
+ *
+ * The session will no longer have pending changes after this call returns. Files may then still
+ * be modified through this session to create another commit.
+ *
+ * May return service-specific errors:
+ * - ERR_BAD_TRANSACTION
+ */
+ void commitChanges();
+
+ /**
+ * Abandons any pending changes made through this session.
+ *
+ * The session can then be reused to make new changes.
+ */
+ void abandonChanges();
+
+ /**
+ * Opens a secure file for writing and/or reading.
+ *
+ * Changes made to the file are part of the current transaction. Dropping this session
+ * invalidates the returned `IFile` interface
+ *
+ * @filePath:
+ * path to the file, relative to filesystem root
+ * @options:
+ * options controlling opening behavior
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND
+ * - ERR_ALREADY_EXISTS
+ * - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+ * does not acknowledge
+ */
+ IFile openFile(in @utf8InCpp String filePath, in OpenOptions options);
+
+ /**
+ * Delete a file.
+ *
+ * @filePath:
+ * path to the file, relative to filesystem root
+ * @options:
+ * options controlling deletion behavior
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND
+ * - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+ * does not acknowledge
+ */
+ void deleteFile(in @utf8InCpp String filePath, in DeleteOptions options);
+
+ /**
+ * Renames an existing file.
+ *
+ * The file must not already be opened. (If it is, use `IFile::rename`.)
+ *
+ * @currentPath:
+ * path to the file, relative to filesystem root
+ * @destPath:
+ * the file's new path, relative to filesystem root
+ * @options:
+ * options controlling rename behavior
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND if no file exists at @currentPath, or if @options.destCreateMode is
+ * `NO_CREATE` and no file exists at @destPath
+ * - ERR_ALREADY_EXISTS if @options.destCreateMode is `CREATE_EXCLUSIVE` and a file exists at
+ * @destPath
+ * - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+ * does not acknowledge
+ */
+ void renameFile(in @utf8InCpp String currentPath, in @utf8InCpp String destPath,
+ in RenameOptions options);
+
+ /**
+ * Opens a directory from a filesystem with the given properties.
+ *
+ * Dropping this session invalidates the returned `IDir` interface.
+ *
+ * @path:
+ * path to the directory, relative to filesystem root
+ * @readIntegrity:
+ * allow opening (and subsequent read/write operations) despite possible tampering for the
+ * directory
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND
+ * - ERR_FS_* if the filesystem has been tampered with in a way that @readIntegrity does not
+ * acknowledge
+ */
+ IDir openDir(in @utf8InCpp String path, in ReadIntegrity readIntegrity);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl
new file mode 100644
index 0000000..997ca62
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+import android.hardware.security.see.storage.FileMode;
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable OpenOptions {
+ /** Controls creation behavior of the to-be-opened file. See `CreationMode` docs for details. */
+ CreationMode createMode = CreationMode.NO_CREATE;
+
+ /** Controls access behavior of the to-be-opened file. See `FileMode` docs for details. */
+ FileMode accessMode = FileMode.READ_WRITE;
+
+ /**
+ * Set to acknowledge possible files tampering.
+ *
+ * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+ * service-specific code.
+ */
+ ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+ /**
+ * If this file already exists, discard existing content and open
+ * it as a new file. No semantic change if the file does not exist.
+ */
+ boolean truncateOnOpen;
+
+ /**
+ * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+ *
+ * If the A/B update fails, the operation will be rolled back. This rollback will not
+ * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+ * acknowledged by setting the `readIntegrity`.
+ */
+ boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl
new file mode 100644
index 0000000..cc0e4f9
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+enum ReadIntegrity {
+ /**
+ * Return an error on reads if any REE alteration of the written data
+ * has been detected.
+ */
+ NO_TAMPER,
+
+ /**
+ * Return an error on reads if any REE alteration other than a reset
+ * has been detected.
+ */
+ IGNORE_RESET,
+
+ /**
+ * Return an error if any REE alteration other than a rollback to a
+ * valid checkpoint has been detected. (What makes a checkpoint valid is
+ * implementation defined; an implementation might take a checkpoint on its
+ * first post-factory boot. A reset is a rollback to the initial state.)
+ */
+ IGNORE_ROLLBACK,
+
+ // There's no `IGNORE_ALL` because if REE has done any alteration other
+ // than a rollback, the file contents will be known-bad data.
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl
new file mode 100644
index 0000000..f55ea7f
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable RenameOptions {
+ /** Controls creation behavior of the dest file. See `CreationMode` docs for details. */
+ CreationMode destCreateMode = CreationMode.CREATE_EXCLUSIVE;
+
+ /**
+ * Set to acknowledge possible files tampering.
+ *
+ * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+ * service-specific code.
+ */
+ ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+ /**
+ * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+ *
+ * If the A/B update fails, the operation will be rolled back. This rollback will not
+ * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+ * acknowledged by setting the `readIntegrity`.
+ */
+ boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl
new file mode 100644
index 0000000..0a39fdd
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+/** Specifies types of REE tampering the filesystem may detect */
+enum Tamper {
+ /** REE has reset this file or the containing file system. */
+ RESET,
+
+ /** REE has rolled back this file or the containing file system to a previous state. */
+ ROLLBACK,
+
+ /** REE has made some other modification to the file. */
+ OTHER,
+}
diff --git a/thermal/aidl/vts/VtsHalThermalTargetTest.cpp b/thermal/aidl/vts/VtsHalThermalTargetTest.cpp
index 403c6c8..4208d09 100644
--- a/thermal/aidl/vts/VtsHalThermalTargetTest.cpp
+++ b/thermal/aidl/vts/VtsHalThermalTargetTest.cpp
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
#include <thread>
+#include <unordered_map>
#include <vector>
#define LOG_TAG "thermal_aidl_hal_test"
@@ -333,6 +334,49 @@
}
}
+// Test Thermal->getTemperatureThresholdsWithType(SKIN).
+// @VsrTest = GMS-VSR-3.2.5-001
+// @VsrTest = VSR-3.2.5-001
+// @VsrTest = GMS-VSR-3.2.5-002
+// @VsrTest = VSR-3.2.5-002
+TEST_P(ThermalAidlTest, SkinTemperatureThresholdsTest) {
+ auto apiLevel = ::android::base::GetIntProperty<int32_t>("ro.vendor.api_level", 0);
+ if (apiLevel < 35) {
+ GTEST_SKIP() << "Skipping test as the vendor level is below 35: " << apiLevel;
+ }
+ std::vector<Temperature> temperatures;
+ ::ndk::ScopedAStatus status =
+ mThermal->getTemperaturesWithType(TemperatureType::SKIN, &temperatures);
+ ASSERT_TRUE(status.isOk()) << "getTemperaturesWithType(SKIN) failed";
+ ASSERT_FALSE(temperatures.empty()) << "getTemperaturesWithType(SKIN) returns empty";
+ ASSERT_EQ(1, temperatures.size())
+ << "getTemperaturesWithType(SKIN) returns multiple temperatures";
+
+ std::vector<TemperatureThreshold> thresholds;
+ status = mThermal->getTemperatureThresholdsWithType(TemperatureType::SKIN, &thresholds);
+ ASSERT_TRUE(status.isOk()) << "getTemperatureThresholdsWithType(SKIN) failed";
+ ASSERT_FALSE(thresholds.empty()) << "getTemperatureThresholdsWithType(SKIN) returns empty";
+ ASSERT_EQ(1, thresholds.size())
+ << "getTemperatureThresholdsWithType(SKIN) returns multiple thresholds";
+ auto temperature = temperatures[0];
+ auto threshold = thresholds[0];
+ ASSERT_EQ(temperature.name, threshold.name);
+ auto severities = ::ndk::enum_range<ThrottlingSeverity>();
+ auto cardinality = std::distance(severities.begin(), severities.end());
+ ASSERT_NE(NAN, temperature.value);
+ ASSERT_EQ(cardinality, threshold.hotThrottlingThresholds.size());
+ float lastThreshold = threshold.hotThrottlingThresholds[1];
+ // skip NONE, and check that the rest should be set and non-decreasing
+ for (auto i = 2; i < cardinality; i++) {
+ float t = threshold.hotThrottlingThresholds[i];
+ ASSERT_NE(NAN, t);
+ ASSERT_TRUE(t >= lastThreshold) << "Temperature thresholds should be non-decreasing "
+ << "but got " << t << " for status " << i << " and "
+ << lastThreshold << " for status " << i - 1;
+ lastThreshold = t;
+ }
+}
+
// Test Thermal->getCoolingDevices().
TEST_P(ThermalAidlTest, CoolingDeviceTest) {
std::vector<CoolingDevice> ret;
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
index 0f0c77e..d9e023c 100644
--- a/wifi/aidl/default/aidl_struct_util.cpp
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -1085,6 +1085,41 @@
return true;
}
+// TODO (b/324519882): Remove logs after validating the structure size.
+void logAidlLinkLayerStatsSize(StaLinkLayerStats& aidl_stats) {
+ unsigned long expectedMaxRadios = 5;
+ unsigned long expectedMaxLinks = 5;
+ unsigned long expectedMaxChannelStats = 512;
+ unsigned long expectedMaxPeers = 3;
+ unsigned long expectedMaxRateStats = 1024;
+
+ unsigned long maxChannelStats = 0, maxPeers = 0, maxRateStats = 0;
+ for (size_t i = 0; i < aidl_stats.radios.size(); i++) {
+ maxChannelStats =
+ std::max(maxChannelStats, (unsigned long)aidl_stats.radios[i].channelStats.size());
+ }
+ for (size_t i = 0; i < aidl_stats.iface.links.size(); i++) {
+ maxPeers = std::max(maxPeers, (unsigned long)aidl_stats.iface.links[i].peers.size());
+ for (size_t j = 0; j < aidl_stats.iface.links[i].peers.size(); j++) {
+ maxRateStats =
+ std::max(maxRateStats,
+ (unsigned long)aidl_stats.iface.links[i].peers[j].rateStats.size());
+ }
+ }
+
+ if (aidl_stats.radios.size() > expectedMaxRadios ||
+ aidl_stats.iface.links.size() > expectedMaxLinks ||
+ maxChannelStats > expectedMaxChannelStats || maxPeers > expectedMaxPeers ||
+ maxRateStats > expectedMaxRateStats) {
+ LOG(INFO) << "StaLinkLayerStats exceeds expected vector size";
+ LOG(INFO) << " numRadios: " << aidl_stats.radios.size();
+ LOG(INFO) << " numLinks: " << aidl_stats.iface.links.size();
+ LOG(INFO) << " maxChannelStats: " << maxChannelStats;
+ LOG(INFO) << " maxPeers: " << maxPeers;
+ LOG(INFO) << " maxRateStats: " << maxRateStats;
+ }
+}
+
bool convertLegacyPeerInfoStatsToAidl(const legacy_hal::WifiPeerInfo& legacy_peer_info_stats,
StaPeerInfo* aidl_peer_info_stats) {
if (!aidl_peer_info_stats) {
diff --git a/wifi/aidl/default/aidl_struct_util.h b/wifi/aidl/default/aidl_struct_util.h
index 7089363..2574f95 100644
--- a/wifi/aidl/default/aidl_struct_util.h
+++ b/wifi/aidl/default/aidl_struct_util.h
@@ -94,6 +94,7 @@
StaLinkLayerStats* aidl_stats);
bool convertLegacyLinkLayerStatsToAidl(const legacy_hal::LinkLayerStats& legacy_stats,
StaLinkLayerStats* aidl_stats);
+void logAidlLinkLayerStatsSize(StaLinkLayerStats& aidl_stats);
bool convertLegacyRoamingCapabilitiesToAidl(
const legacy_hal::wifi_roaming_capabilities& legacy_caps,
StaRoamingCapabilities* aidl_caps);
diff --git a/wifi/aidl/default/wifi_sta_iface.cpp b/wifi/aidl/default/wifi_sta_iface.cpp
index f0509dc..aee183d 100644
--- a/wifi/aidl/default/wifi_sta_iface.cpp
+++ b/wifi/aidl/default/wifi_sta_iface.cpp
@@ -435,6 +435,7 @@
} else {
return {StaLinkLayerStats{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
}
+ aidl_struct_util::logAidlLinkLayerStatsSize(aidl_stats);
return {aidl_stats, ndk::ScopedAStatus::ok()};
}
diff --git a/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp b/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
index 986e3a8..21d50ac 100644
--- a/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
+++ b/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
@@ -62,6 +62,23 @@
int mode_id;
return configureChipToSupportConcurrencyTypeInternal(wifi_chip, type, &mode_id);
}
+
+OuiKeyedData generateOuiKeyedData(int oui) {
+ PersistableBundle bundle;
+ bundle.putString("stringKey", "stringValue");
+ bundle.putInt("intKey", 12345);
+
+ OuiKeyedData data;
+ data.oui = oui;
+ data.vendorData = bundle;
+ return data;
+}
+
+// Wraps generateOuiKeyedData result in std::optional
+std::optional<OuiKeyedData> generateOuiKeyedDataOptional(int oui) {
+ return std::optional<OuiKeyedData>{generateOuiKeyedData(oui)};
+}
+
} // namespace
bool checkStatusCode(ndk::ScopedAStatus* status, WifiStatusCode expected_code) {
@@ -238,3 +255,20 @@
bool isAidlServiceAvailable(const char* instance_name) {
return AServiceManager_isDeclared(instance_name);
}
+
+std::vector<OuiKeyedData> generateOuiKeyedDataList(int size) {
+ std::vector<OuiKeyedData> dataList;
+ for (int i = 0; i < size; i++) {
+ dataList.push_back(generateOuiKeyedData(i + 1));
+ }
+ return dataList;
+}
+
+// Generate OuiKeyedData list fully wrapped in std::optional
+std::optional<std::vector<std::optional<OuiKeyedData>>> generateOuiKeyedDataListOptional(int size) {
+ std::vector<std::optional<OuiKeyedData>> dataList;
+ for (int i = 0; i < size; i++) {
+ dataList.push_back(generateOuiKeyedDataOptional(i + 1));
+ }
+ return std::optional<std::vector<std::optional<OuiKeyedData>>>{dataList};
+}
diff --git a/wifi/aidl/vts/functional/wifi_aidl_test_utils.h b/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
index 921d689..1369dd4 100644
--- a/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
+++ b/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
@@ -21,6 +21,7 @@
#include <aidl/android/hardware/wifi/IWifi.h>
#include <aidl/android/hardware/wifi/IWifiChip.h>
#include <android/binder_manager.h>
+#include <android/persistable_bundle_aidl.h>
#include <wifi_system/interface_tool.h>
using aidl::android::hardware::wifi::IfaceConcurrencyType;
@@ -30,6 +31,8 @@
using aidl::android::hardware::wifi::IWifiNanIface;
using aidl::android::hardware::wifi::IWifiStaIface;
using aidl::android::hardware::wifi::WifiStatusCode;
+using aidl::android::hardware::wifi::common::OuiKeyedData;
+using aidl::android::os::PersistableBundle;
// Helper functions to obtain references to the various AIDL interface objects.
std::shared_ptr<IWifi> getWifi(const char* instance_name);
@@ -50,3 +53,6 @@
int32_t getChipFeatureSet(const std::shared_ptr<IWifiChip>& wifi_chip);
bool checkStatusCode(ndk::ScopedAStatus* status, WifiStatusCode expected_code);
bool isAidlServiceAvailable(const char* instance_name);
+// Generate test vendor data.
+std::vector<OuiKeyedData> generateOuiKeyedDataList(int size);
+std::optional<std::vector<std::optional<OuiKeyedData>>> generateOuiKeyedDataListOptional(int size);
diff --git a/wifi/aidl/vts/functional/wifi_nan_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_nan_iface_aidl_test.cpp
index 738e72c..bc169a4 100644
--- a/wifi/aidl/vts/functional/wifi_nan_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_nan_iface_aidl_test.cpp
@@ -22,6 +22,7 @@
#include <aidl/android/hardware/wifi/BnWifi.h>
#include <aidl/android/hardware/wifi/BnWifiNanIfaceEventCallback.h>
#include <aidl/android/hardware/wifi/NanBandIndex.h>
+#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
#include <binder/IServiceManager.h>
@@ -60,6 +61,10 @@
#define TIMEOUT_PERIOD 10
+namespace {
+const auto& kTestVendorDataOptional = generateOuiKeyedDataListOptional(5);
+}
+
class WifiNanIfaceAidlTest : public testing::TestWithParam<std::string> {
public:
void SetUp() override {
@@ -72,6 +77,7 @@
std::shared_ptr<WifiNanIfaceEventCallback> callback =
ndk::SharedRefBase::make<WifiNanIfaceEventCallback>(*this);
EXPECT_TRUE(wifi_nan_iface_->registerEventCallback(callback).isOk());
+ EXPECT_TRUE(wifi_nan_iface_->getInterfaceVersion(&interface_version_).isOk());
}
void TearDown() override { stopWifiService(getInstanceName()); }
@@ -401,6 +407,7 @@
protected:
std::shared_ptr<IWifiNanIface> wifi_nan_iface_;
+ int interface_version_;
uint64_t callback_event_bitmap_;
uint16_t id_;
uint8_t session_id_;
@@ -640,6 +647,10 @@
nanPublishRequest.autoAcceptDataPathRequests = false;
nanPublishRequest.publishType = NanPublishType::UNSOLICITED;
nanPublishRequest.txType = NanTxType::BROADCAST;
+ if (interface_version_ >= 2) {
+ LOG(INFO) << "Including vendor data in Publish request";
+ nanPublishRequest.vendorData = kTestVendorDataOptional;
+ }
status = wifi_nan_iface_->startPublishRequest(inputCmdId + 1, nanPublishRequest);
if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
diff --git a/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
index 4aedc0e..9c6a29e 100644
--- a/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
@@ -21,6 +21,7 @@
#include <aidl/Vintf.h>
#include <aidl/android/hardware/wifi/BnWifi.h>
#include <aidl/android/hardware/wifi/BnWifiRttControllerEventCallback.h>
+#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
#include <binder/IServiceManager.h>
@@ -42,6 +43,10 @@
using aidl::android::hardware::wifi::WifiChannelWidthInMhz;
using aidl::android::hardware::wifi::WifiStatusCode;
+namespace {
+const auto& kTestVendorDataOptional = generateOuiKeyedDataListOptional(5);
+}
+
class WifiRttControllerAidlTest : public testing::TestWithParam<std::string> {
public:
void SetUp() override {
@@ -50,6 +55,7 @@
stopWifiService(getInstanceName());
wifi_rtt_controller_ = getWifiRttController();
ASSERT_NE(nullptr, wifi_rtt_controller_.get());
+ ASSERT_TRUE(wifi_rtt_controller_->getInterfaceVersion(&interface_version_).isOk());
// Check RTT support before we run the test.
RttCapabilities caps = {};
@@ -82,6 +88,7 @@
}
std::shared_ptr<IWifiRttController> wifi_rtt_controller_;
+ int interface_version_;
private:
const char* getInstanceName() { return GetParam().c_str(); }
@@ -226,6 +233,10 @@
config.numRetriesPerRttFrame = 3;
config.numRetriesPerFtmr = 3;
config.burstDuration = 9;
+ if (interface_version_ >= 2) {
+ LOG(INFO) << "Including vendor data in Rtt Config";
+ config.vendorData = kTestVendorDataOptional;
+ }
int cmdId = 55;
std::vector<RttConfig> configs = {config};
diff --git a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
index fa7149f..e8e19ae 100644
--- a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -29,6 +29,7 @@
#include "wifi_aidl_test_utils.h"
+using aidl::android::hardware::wifi::CachedScanData;
using aidl::android::hardware::wifi::IWifi;
using aidl::android::hardware::wifi::IWifiStaIface;
using aidl::android::hardware::wifi::MacAddress;
@@ -74,7 +75,17 @@
return testing::deviceSupportsFeature("com.google.android.tv.mdns_offload");
}
- // Detected panel TV device by using ro.oem.key1 property.
+ bool doesDeviceSupportFullNetworkingUnder2w() {
+ return testing::deviceSupportsFeature("com.google.android.tv.full_networking_under_2w");
+ }
+
+ // Detect TV devices.
+ bool isTvDevice() {
+ return testing::deviceSupportsFeature("android.software.leanback") ||
+ testing::deviceSupportsFeature("android.hardware.type.television");
+ }
+
+ // Detect Panel TV devices by using ro.oem.key1 property.
// https://docs.partner.android.com/tv/build/platform/props-vars/ro-oem-key1
bool isPanelTvDevice() {
const std::string oem_key1 = getPropertyString("ro.oem.key1");
@@ -135,10 +146,23 @@
*/
// @VsrTest = 5.3.12
TEST_P(WifiStaIfaceAidlTest, CheckApfIsSupported) {
- // Flat panel TV devices that support MDNS offload do not have to implement APF if the WiFi
- // chipset does not have sufficient RAM to do so.
- if (isPanelTvDevice() && isMdnsOffloadPresentInNIC()) {
- GTEST_SKIP() << "Panel TV supports mDNS offload. It is not required to support APF";
+ const std::string oem_key1 = getPropertyString("ro.oem.key1");
+ if (isTvDevice()) {
+ // Flat panel TV devices that support MDNS offload do not have to implement APF if the WiFi
+ // chipset does not have sufficient RAM to do so.
+ if (isPanelTvDevice() && isMdnsOffloadPresentInNIC()) {
+ GTEST_SKIP() << "Panel TV supports mDNS offload. It is not required to support APF";
+ }
+ // For TV devices declaring the
+ // com.google.android.tv.full_networking_under_2w feature, this indicates
+ // the device can meet the <= 2W standby power requirement while
+ // continuously processing network packets on the CPU, even in standby mode.
+ // In these cases, APF support is strongly recommended rather than being
+ // mandatory.
+ if (doesDeviceSupportFullNetworkingUnder2w()) {
+ GTEST_SKIP() << "TV Device meets the <= 2W standby power demand requirement. It is not "
+ "required to support APF.";
+ }
}
int vendor_api_level = property_get_int32("ro.vendor.api_level", 0);
// Before VSR 14, APF support is optional.
@@ -315,6 +339,23 @@
}
}
+/*
+ * CachedScanData
+ */
+TEST_P(WifiStaIfaceAidlTest, CachedScanData) {
+ if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::CACHED_SCAN_DATA)) {
+ GTEST_SKIP() << "Cached scan data is not supported.";
+ }
+
+ // Retrieve cached scan data.
+ CachedScanData cached_scan_data = {};
+ EXPECT_TRUE(wifi_sta_iface_->getCachedScanData(&cached_scan_data).isOk());
+
+ if (cached_scan_data.cachedScanResults.size() > 0) {
+ EXPECT_GT(cached_scan_data.cachedScanResults[0].frequencyMhz, 0);
+ }
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiStaIfaceAidlTest);
INSTANTIATE_TEST_SUITE_P(WifiTest, WifiStaIfaceAidlTest,
testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)),
diff --git a/wifi/hostapd/aidl/vts/functional/Android.bp b/wifi/hostapd/aidl/vts/functional/Android.bp
index 9fbbf4b..f614679 100644
--- a/wifi/hostapd/aidl/vts/functional/Android.bp
+++ b/wifi/hostapd/aidl/vts/functional/Android.bp
@@ -21,7 +21,7 @@
"libvndksupport",
],
static_libs: [
- "android.hardware.wifi.hostapd-V1-ndk",
+ "android.hardware.wifi.hostapd-V2-ndk",
"VtsHalWifiV1_0TargetTestUtil",
"VtsHalWifiV1_5TargetTestUtil",
"VtsHalWifiV1_6TargetTestUtil",
diff --git a/wifi/hostapd/aidl/vts/functional/VtsHalHostapdTargetTest.cpp b/wifi/hostapd/aidl/vts/functional/VtsHalHostapdTargetTest.cpp
index 137537d..590c58b 100644
--- a/wifi/hostapd/aidl/vts/functional/VtsHalHostapdTargetTest.cpp
+++ b/wifi/hostapd/aidl/vts/functional/VtsHalHostapdTargetTest.cpp
@@ -58,6 +58,7 @@
const int kIfaceInvalidChannel = 567;
const std::vector<uint8_t> kTestZeroMacAddr(6, 0x0);
const Ieee80211ReasonCode kTestDisconnectReasonCode = Ieee80211ReasonCode::WLAN_REASON_UNSPECIFIED;
+const auto& kTestVendorDataOptional = generateOuiKeyedDataListOptional(5);
inline BandMask operator|(BandMask a, BandMask b) {
return static_cast<BandMask>(static_cast<int32_t>(a) |
@@ -74,6 +75,7 @@
hostapd = getHostapd(GetParam());
ASSERT_NE(hostapd, nullptr);
EXPECT_TRUE(hostapd->setDebugParams(DebugLevel::EXCESSIVE).isOk());
+ EXPECT_TRUE(hostapd->getInterfaceVersion(&interface_version_).isOk());
isAcsSupport = testing::checkSubstringInCommandOutput(
"/system/bin/cmd wifi get-softap-supported-features",
@@ -98,6 +100,7 @@
bool isAcsSupport;
bool isWpa3SaeSupport;
bool isBridgedSupport;
+ int interface_version_;
IfaceParams getIfaceParamsWithoutAcs(std::string iface_name) {
IfaceParams iface_params;
@@ -343,6 +346,22 @@
}
/**
+ * Adds an access point with Open network config & ACS disabled.
+ * IfaceParams will also include vendor data.
+ * Access point creation should pass.
+ */
+TEST_P(HostapdAidl, AddOpenAccessPointWithVendorData) {
+ if (interface_version_ < 2) {
+ GTEST_SKIP() << "Vendor data is available in IfaceParams as of Hostapd V2";
+ }
+ std::string ifname = setupApIfaceAndGetName(false);
+ IfaceParams params = getIfaceParamsWithoutAcs(ifname);
+ params.vendorData = kTestVendorDataOptional;
+ auto status = hostapd->addAccessPoint(params, getOpenNwParams());
+ EXPECT_TRUE(status.isOk());
+}
+
+/**
* Adds an access point with SAE Transition network config & ACS disabled.
* Access point creation should pass.
*/
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
index 82e3128..8f1c4bd 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
@@ -27,6 +27,7 @@
#include <cutils/properties.h>
#include "supplicant_test_utils.h"
+#include "wifi_aidl_test_utils.h"
using aidl::android::hardware::wifi::supplicant::BnSupplicantP2pIfaceCallback;
using aidl::android::hardware::wifi::supplicant::DebugLevel;
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
index 5d00485..58f9be8 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
@@ -43,6 +43,10 @@
using aidl::android::hardware::wifi::supplicant::ISupplicantStaIface;
using aidl::android::hardware::wifi::supplicant::ISupplicantStaNetwork;
using aidl::android::hardware::wifi::supplicant::KeyMgmtMask;
+using aidl::android::hardware::wifi::supplicant::MscsParams;
+using aidl::android::hardware::wifi::supplicant::QosCharacteristics;
+using aidl::android::hardware::wifi::supplicant::QosPolicyScsData;
+using aidl::android::hardware::wifi::supplicant::QosPolicyScsRequestStatus;
using aidl::android::hardware::wifi::supplicant::WpaDriverCapabilitiesMask;
using aidl::android::hardware::wifi::supplicant::WpsConfigMethods;
using android::ProcessState;
@@ -252,6 +256,7 @@
true, // show timestamps
true)
.isOk());
+ ASSERT_TRUE(supplicant_->getInterfaceVersion(&interface_version_).isOk());
EXPECT_TRUE(supplicant_->getStaInterface(getStaIfaceName(), &sta_iface_)
.isOk());
ASSERT_NE(sta_iface_, nullptr);
@@ -293,6 +298,7 @@
protected:
std::shared_ptr<ISupplicant> supplicant_;
std::shared_ptr<ISupplicantStaIface> sta_iface_;
+ int interface_version_;
private:
// synchronization objects
@@ -788,6 +794,55 @@
EXPECT_TRUE(sta_iface_->removeDppUri(peer_id).isOk());
}
+/*
+ * Configure and Disable MSCS
+ */
+TEST_P(SupplicantStaIfaceAidlTest, ConfigureAndDisableMscs) {
+ if (interface_version_ < 3) {
+ GTEST_SKIP() << "MSCS configure/disable is available as of Supplicant V3";
+ }
+ MscsParams params;
+ params.upBitmap = 0;
+ params.upLimit = 7;
+ params.streamTimeoutUs = 1000; // 1 ms
+ params.frameClassifierMask = 0;
+ EXPECT_TRUE(sta_iface_->configureMscs(params).isOk());
+ EXPECT_TRUE(sta_iface_->disableMscs().isOk());
+}
+
+/*
+ * Add and remove QoS policy with traffic characteristics
+ */
+TEST_P(SupplicantStaIfaceAidlTest, AddAndRemoveQosWithTrafficChars) {
+ if (interface_version_ < 3) {
+ GTEST_SKIP() << "QosCharacteristics is available as of Supplicant V3";
+ }
+
+ QosCharacteristics qosChars;
+ qosChars.minServiceIntervalUs = 2000;
+ qosChars.maxServiceIntervalUs = 5000;
+ qosChars.minDataRateKbps = 500;
+ qosChars.delayBoundUs = 200;
+ qosChars.optionalFieldMask = 0; // no optional fields
+
+ uint8_t policyId = 5;
+ QosPolicyScsData qosPolicy;
+ qosPolicy.policyId = policyId;
+ qosPolicy.direction = QosPolicyScsData::LinkDirection::UPLINK;
+ qosPolicy.QosCharacteristics = qosChars;
+
+ std::vector<uint8_t> policyIdList{policyId};
+ std::vector<QosPolicyScsData> policyList{qosPolicy};
+ std::vector<QosPolicyScsRequestStatus> responseList;
+
+ // Check that we receive some reply for this request.
+ // Policy may not be accepted (ex. policy with this id already exists).
+ EXPECT_TRUE(sta_iface_->addQosPolicyRequestForScs(policyList, &responseList).isOk());
+ EXPECT_EQ(1, responseList.size());
+ EXPECT_TRUE(sta_iface_->removeQosPolicyForScs(policyIdList, &responseList).isOk());
+ EXPECT_EQ(1, responseList.size());
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceAidlTest);
INSTANTIATE_TEST_SUITE_P(Supplicant, SupplicantStaIfaceAidlTest,
testing::ValuesIn(android::getAidlHalInstanceNames(
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
index a541f8f..9bdd2f5 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
@@ -27,6 +27,7 @@
#include <cutils/properties.h>
#include "supplicant_test_utils.h"
+#include "wifi_aidl_test_utils.h"
using aidl::android::hardware::wifi::supplicant::AuthAlgMask;
using aidl::android::hardware::wifi::supplicant::BnSupplicantStaNetworkCallback;
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_test_utils.h b/wifi/supplicant/aidl/vts/functional/supplicant_test_utils.h
index 51793fd..e39e2f4 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_test_utils.h
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_test_utils.h
@@ -16,18 +16,14 @@
#pragma once
-#include <android/persistable_bundle_aidl.h>
-
#include "supplicant_aidl_test_utils.h"
#include "supplicant_legacy_test_utils.h"
-using aidl::android::hardware::wifi::common::OuiKeyedData;
using aidl::android::hardware::wifi::supplicant::IfaceInfo;
using aidl::android::hardware::wifi::supplicant::ISupplicant;
using aidl::android::hardware::wifi::supplicant::ISupplicantP2pIface;
using aidl::android::hardware::wifi::supplicant::ISupplicantStaIface;
using aidl::android::hardware::wifi::supplicant::KeyMgmtMask;
-using aidl::android::os::PersistableBundle;
std::string getStaIfaceName() {
std::array<char, PROPERTY_VALUE_MAX> buffer;
@@ -101,36 +97,3 @@
std::copy(vectorAddr.begin(), vectorAddr.begin() + 6, arrayAddr.begin());
return arrayAddr;
}
-
-OuiKeyedData generateOuiKeyedData(int oui) {
- PersistableBundle bundle;
- bundle.putString("stringKey", "stringValue");
- bundle.putInt("intKey", 12345);
-
- OuiKeyedData data;
- data.oui = oui;
- data.vendorData = bundle;
- return data;
-}
-
-std::vector<OuiKeyedData> generateOuiKeyedDataList(int size) {
- std::vector<OuiKeyedData> dataList;
- for (int i = 0; i < size; i++) {
- dataList.push_back(generateOuiKeyedData(i + 1));
- }
- return dataList;
-}
-
-// Wraps generateOuiKeyedData result in std::optional
-std::optional<OuiKeyedData> generateOuiKeyedDataOptional(int oui) {
- return std::optional<OuiKeyedData>{generateOuiKeyedData(oui)};
-}
-
-// Generate OuiKeyedData list fully wrapped in std::optional
-std::optional<std::vector<std::optional<OuiKeyedData>>> generateOuiKeyedDataListOptional(int size) {
- std::vector<std::optional<OuiKeyedData>> dataList;
- for (int i = 0; i < size; i++) {
- dataList.push_back(generateOuiKeyedDataOptional(i + 1));
- }
- return std::optional<std::vector<std::optional<OuiKeyedData>>>{dataList};
-}