Merge "Add HAL changes for AUTOMATIC_EMERGENCY_BRAKING_ENABLED"
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl
index a8c58c1..77063df 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl
@@ -36,4 +36,21 @@
interface ITelephony {
android.hardware.audio.core.AudioMode[] getSupportedAudioModes();
void switchAudioMode(android.hardware.audio.core.AudioMode mode);
+ android.hardware.audio.core.ITelephony.TelecomConfig setTelecomConfig(in android.hardware.audio.core.ITelephony.TelecomConfig config);
+ @JavaDerive(equals=true, toString=true) @VintfStability
+ parcelable TelecomConfig {
+ @nullable android.media.audio.common.Float voiceVolume;
+ android.hardware.audio.core.ITelephony.TelecomConfig.TtyMode ttyMode = android.hardware.audio.core.ITelephony.TelecomConfig.TtyMode.UNSPECIFIED;
+ @nullable android.media.audio.common.Boolean isHacEnabled;
+ const int VOICE_VOLUME_MIN = 0;
+ const int VOICE_VOLUME_MAX = 1;
+ @Backing(type="int") @VintfStability
+ enum TtyMode {
+ UNSPECIFIED = (-1),
+ OFF = 0,
+ FULL = 1,
+ HCO = 2,
+ VCO = 3,
+ }
+ }
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
index 40a8d72..959594b 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
@@ -35,7 +35,7 @@
@VintfStability
union HapticGenerator {
android.hardware.audio.effect.VendorExtension vendorExtension;
- android.hardware.audio.effect.HapticGenerator.HapticScale hapticScale;
+ android.hardware.audio.effect.HapticGenerator.HapticScale[] hapticScales;
android.hardware.audio.effect.HapticGenerator.VibratorInformation vibratorInfo;
@VintfStability
union Id {
diff --git a/audio/aidl/android/hardware/audio/core/ITelephony.aidl b/audio/aidl/android/hardware/audio/core/ITelephony.aidl
index a872c7c..a817032 100644
--- a/audio/aidl/android/hardware/audio/core/ITelephony.aidl
+++ b/audio/aidl/android/hardware/audio/core/ITelephony.aidl
@@ -17,6 +17,8 @@
package android.hardware.audio.core;
import android.hardware.audio.core.AudioMode;
+import android.media.audio.common.Boolean;
+import android.media.audio.common.Float;
/**
* An instance of ITelephony manages settings which are specific to voice calls
@@ -53,4 +55,75 @@
* @throws EX_ILLEGAL_STATE If there was an error during switching.
*/
void switchAudioMode(AudioMode mode);
+
+ @JavaDerive(equals=true, toString=true)
+ @VintfStability
+ parcelable TelecomConfig {
+ const int VOICE_VOLUME_MIN = 0;
+ const int VOICE_VOLUME_MAX = 1;
+ /**
+ * Volume of a voice call. 1.0f means unity gain, 0.0f is muted,
+ * see VOLUME_* constants.
+ */
+ @nullable Float voiceVolume;
+ /**
+ * The current mode of teletypewritter (TTY).
+ */
+ @VintfStability
+ @Backing(type="int")
+ enum TtyMode {
+ /**
+ * The default uninitialized value.
+ */
+ UNSPECIFIED = -1,
+ /**
+ * TTY mode is off.
+ */
+ OFF = 0,
+ /**
+ * TTY mode is on. The speaker is off and the microphone is muted. The
+ * user will communicate with the remote party by sending and receiving
+ * text messages.
+ */
+ FULL = 1,
+ /**
+ * TTY mode is in hearing carryover mode (HCO). The microphone is muted
+ * but the speaker is on. The user will communicate with the remote
+ * party by sending text messages and hearing an audible reply.
+ */
+ HCO = 2,
+ /**
+ * TTY mode is in voice carryover mode (VCO). The speaker is off but the
+ * microphone is still on. User will communicate with the remote party
+ * by speaking and receiving text message replies.
+ */
+ VCO = 3,
+ }
+ TtyMode ttyMode = TtyMode.UNSPECIFIED;
+ /**
+ * Whether Hearing Aid Compatibility - Telecoil (HAC-T) mode is enabled.
+ */
+ @nullable Boolean isHacEnabled;
+ }
+
+ /**
+ * Set the configuration of the telephony audio.
+ *
+ * In the provided parcelable, the client sets zero, one or more parameters
+ * which have to be updated on the HAL side. The parameters that are left
+ * unset must retain their current values.
+ *
+ * In the returned parcelable, all parameter fields known to the HAL module
+ * must be populated to their current values.The client can pass an
+ * uninitialized parcelable in order to retrieve the current configuration.
+ *
+ * @return The current configuration (after update). All fields known to
+ * the HAL must be populated.
+ * @param config The configuration to set. Any number of fields may be left
+ * uninitialized.
+ * @throws EX_UNSUPPORTED_OPERATION If telephony is not supported.
+ * @throws EX_ILLEGAL_ARGUMENT If the requested combination of parameter
+ * values is invalid.
+ */
+ TelecomConfig setTelecomConfig(in TelecomConfig config);
}
diff --git a/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl b/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
index 944155f..3063ee3 100644
--- a/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
+++ b/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
@@ -90,6 +90,6 @@
float maxAmplitude;
}
- HapticScale hapticScale;
+ HapticScale[] hapticScales;
VibratorInformation vibratorInfo;
}
diff --git a/audio/aidl/default/Telephony.cpp b/audio/aidl/default/Telephony.cpp
index 1854b35..d873178 100644
--- a/audio/aidl/default/Telephony.cpp
+++ b/audio/aidl/default/Telephony.cpp
@@ -20,8 +20,17 @@
#include "core-impl/Telephony.h"
+using aidl::android::media::audio::common::Boolean;
+using aidl::android::media::audio::common::Float;
+
namespace aidl::android::hardware::audio::core {
+Telephony::Telephony() {
+ mTelecomConfig.voiceVolume = Float{TelecomConfig::VOICE_VOLUME_MAX};
+ mTelecomConfig.ttyMode = TelecomConfig::TtyMode::OFF;
+ mTelecomConfig.isHacEnabled = Boolean{false};
+}
+
ndk::ScopedAStatus Telephony::getSupportedAudioModes(std::vector<AudioMode>* _aidl_return) {
*_aidl_return = mSupportedAudioModes;
LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
@@ -38,4 +47,28 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
+ndk::ScopedAStatus Telephony::setTelecomConfig(const TelecomConfig& in_config,
+ TelecomConfig* _aidl_return) {
+ if (in_config.voiceVolume.has_value() &&
+ (in_config.voiceVolume.value().value < TelecomConfig::VOICE_VOLUME_MIN ||
+ in_config.voiceVolume.value().value > TelecomConfig::VOICE_VOLUME_MAX)) {
+ LOG(ERROR) << __func__
+ << ": voice volume value is invalid: " << in_config.voiceVolume.value().value;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (in_config.voiceVolume.has_value()) {
+ mTelecomConfig.voiceVolume = in_config.voiceVolume;
+ }
+ if (in_config.ttyMode != TelecomConfig::TtyMode::UNSPECIFIED) {
+ mTelecomConfig.ttyMode = in_config.ttyMode;
+ }
+ if (in_config.isHacEnabled.has_value()) {
+ mTelecomConfig.isHacEnabled = in_config.isHacEnabled;
+ }
+ *_aidl_return = mTelecomConfig;
+ LOG(DEBUG) << __func__ << ": received " << in_config.toString() << ", returning "
+ << _aidl_return->toString();
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp
index f6211c4..3c3b66f 100644
--- a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp
+++ b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.cpp
@@ -90,8 +90,8 @@
auto tag = hgParam.getTag();
switch (tag) {
- case HapticGenerator::hapticScale: {
- RETURN_IF(mContext->setHgHapticScale(hgParam.get<HapticGenerator::hapticScale>()) !=
+ case HapticGenerator::hapticScales: {
+ RETURN_IF(mContext->setHgHapticScales(hgParam.get<HapticGenerator::hapticScales>()) !=
RetCode::SUCCESS,
EX_ILLEGAL_ARGUMENT, "HapticScaleNotSupported");
return ndk::ScopedAStatus::ok();
@@ -133,8 +133,8 @@
HapticGenerator hgParam;
switch (tag) {
- case HapticGenerator::hapticScale: {
- hgParam.set<HapticGenerator::hapticScale>(mContext->getHgHapticScale());
+ case HapticGenerator::hapticScales: {
+ hgParam.set<HapticGenerator::hapticScales>(mContext->getHgHapticScales());
break;
}
case HapticGenerator::vibratorInfo: {
@@ -183,4 +183,20 @@
return {STATUS_OK, samples, samples};
}
+RetCode HapticGeneratorSwContext::setHgHapticScales(
+ const std::vector<HapticGenerator::HapticScale>& hapticScales) {
+ // Assume any audio track ID is valid
+ for (auto& it : hapticScales) {
+ mHapticScales[it.id] = it;
+ }
+ return RetCode::SUCCESS;
+}
+
+std::vector<HapticGenerator::HapticScale> HapticGeneratorSwContext::getHgHapticScales() const {
+ std::vector<HapticGenerator::HapticScale> result;
+ std::transform(mHapticScales.begin(), mHapticScales.end(), std::back_inserter(result),
+ [](auto& scaleIt) { return scaleIt.second; });
+ return result;
+}
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h
index d9ec744..7159501 100644
--- a/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h
+++ b/audio/aidl/default/hapticGenerator/HapticGeneratorSw.h
@@ -33,12 +33,8 @@
LOG(DEBUG) << __func__;
}
- RetCode setHgHapticScale(const HapticGenerator::HapticScale& hapticScale) {
- // All int values are valid for ID
- mHapticScale = hapticScale;
- return RetCode::SUCCESS;
- }
- HapticGenerator::HapticScale getHgHapticScale() const { return mHapticScale; }
+ RetCode setHgHapticScales(const std::vector<HapticGenerator::HapticScale>& hapticScales);
+ std::vector<HapticGenerator::HapticScale> getHgHapticScales() const;
RetCode setHgVibratorInformation(const HapticGenerator::VibratorInformation& vibratorInfo) {
// All float values are valid for resonantFrequencyHz, qFactor, maxAmplitude
@@ -54,7 +50,7 @@
static constexpr float DEFAULT_RESONANT_FREQUENCY = 150.0f;
static constexpr float DEFAULT_Q_FACTOR = 1.0f;
static constexpr float DEFAULT_MAX_AMPLITUDE = 0.0f;
- HapticGenerator::HapticScale mHapticScale = {0, HapticGenerator::VibratorScale::MUTE};
+ std::map<int /* trackID */, HapticGenerator::HapticScale> mHapticScales;
HapticGenerator::VibratorInformation mVibratorInformation = {
DEFAULT_RESONANT_FREQUENCY, DEFAULT_Q_FACTOR, DEFAULT_MAX_AMPLITUDE};
};
diff --git a/audio/aidl/default/include/core-impl/Telephony.h b/audio/aidl/default/include/core-impl/Telephony.h
index 597f3d6..0936172 100644
--- a/audio/aidl/default/include/core-impl/Telephony.h
+++ b/audio/aidl/default/include/core-impl/Telephony.h
@@ -23,12 +23,18 @@
namespace aidl::android::hardware::audio::core {
class Telephony : public BnTelephony {
+ public:
+ Telephony();
+
private:
ndk::ScopedAStatus getSupportedAudioModes(std::vector<AudioMode>* _aidl_return) override;
ndk::ScopedAStatus switchAudioMode(AudioMode in_mode) override;
+ ndk::ScopedAStatus setTelecomConfig(const TelecomConfig& in_config,
+ TelecomConfig* _aidl_return) override;
const std::vector<AudioMode> mSupportedAudioModes = {::ndk::enum_range<AudioMode>().begin(),
::ndk::enum_range<AudioMode>().end()};
+ TelecomConfig mTelecomConfig;
};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 50a93ce..0451c0b 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -84,6 +84,7 @@
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::AudioSource;
using aidl::android::media::audio::common::AudioUsage;
+using aidl::android::media::audio::common::Float;
using aidl::android::media::audio::common::Void;
using android::hardware::audio::common::getChannelCount;
using android::hardware::audio::common::isBitPositionFlagSet;
@@ -1828,6 +1829,46 @@
}
}
+TEST_P(AudioCoreTelephony, TelecomConfig) {
+ static const auto kStatuses = {EX_NONE, EX_UNSUPPORTED_OPERATION};
+ if (telephony == nullptr) {
+ GTEST_SKIP() << "Telephony is not supported";
+ }
+ ndk::ScopedAStatus status;
+ ITelephony::TelecomConfig telecomConfig;
+ ASSERT_STATUS(kStatuses, status = telephony->setTelecomConfig({}, &telecomConfig));
+ if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ GTEST_SKIP() << "Telecom is not supported";
+ }
+ EXPECT_TRUE(telecomConfig.voiceVolume.has_value());
+ EXPECT_NE(ITelephony::TelecomConfig::TtyMode::UNSPECIFIED, telecomConfig.ttyMode);
+ EXPECT_TRUE(telecomConfig.isHacEnabled.has_value());
+ ITelephony::TelecomConfig telecomConfig2;
+ ASSERT_IS_OK(telephony->setTelecomConfig(telecomConfig, &telecomConfig2));
+ EXPECT_EQ(telecomConfig, telecomConfig2);
+}
+
+TEST_P(AudioCoreTelephony, TelecomConfigInvalid) {
+ static const auto kStatuses = {EX_NONE, EX_UNSUPPORTED_OPERATION};
+ if (telephony == nullptr) {
+ GTEST_SKIP() << "Telephony is not supported";
+ }
+ ndk::ScopedAStatus status;
+ ITelephony::TelecomConfig telecomConfig;
+ ASSERT_STATUS(kStatuses, status = telephony->setTelecomConfig({}, &telecomConfig));
+ if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ GTEST_SKIP() << "Telecom is not supported";
+ }
+ EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+ telephony->setTelecomConfig(
+ {.voiceVolume = Float{ITelephony::TelecomConfig::VOICE_VOLUME_MIN - 1}},
+ &telecomConfig));
+ EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
+ telephony->setTelecomConfig(
+ {.voiceVolume = Float{ITelephony::TelecomConfig::VOICE_VOLUME_MAX + 1}},
+ &telecomConfig));
+}
+
using CommandSequence = std::vector<StreamDescriptor::Command>;
class StreamLogicDriverInvalidCommand : public StreamLogicDriver {
public:
diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
index d1f3b97..b8ea9c1 100644
--- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
@@ -19,7 +19,9 @@
#include <Utils.h>
#include <aidl/Vintf.h>
#include <android/binder_enums.h>
-#include <unordered_set>
+#include <map>
+#include <utility>
+#include <vector>
#include "EffectHelper.h"
@@ -87,12 +89,11 @@
ASSERT_NE(nullptr, mFactory);
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
- 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_NO_FATAL_FAILURE(open(mEffect, common, std::nullopt, &ret, EX_NONE));
ASSERT_NE(nullptr, mEffect);
}
@@ -101,15 +102,6 @@
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
}
- Parameter::Specific getDefaultParamSpecific() {
- HapticGenerator::HapticScale hapticScale = {.id = 0,
- .scale = HapticGenerator::VibratorScale::MUTE};
- HapticGenerator hg = HapticGenerator::make<HapticGenerator::hapticScale>(hapticScale);
- Parameter::Specific specific =
- Parameter::Specific::make<Parameter::Specific::hapticGenerator>(hg);
- return specific;
- }
-
static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
std::shared_ptr<IFactory> mFactory;
std::shared_ptr<IEffect> mEffect;
@@ -122,13 +114,13 @@
void SetAndGetHapticGeneratorParameters() {
for (auto& it : mTags) {
- auto& tag = it.first;
- auto& hg = it.second;
+ auto& tag = std::get<ParamTestEnum::PARAM_TEST_TAG>(it);
+ auto& setHg = std::get<ParamTestEnum::PARAM_TEST_TARGET>(it);
// set parameter
Parameter expectParam;
Parameter::Specific specific;
- specific.set<Parameter::Specific::hapticGenerator>(hg);
+ specific.set<Parameter::Specific::hapticGenerator>(setHg);
expectParam.set<Parameter::specific>(specific);
EXPECT_STATUS(EX_NONE, mEffect->setParameter(expectParam)) << expectParam.toString();
@@ -139,15 +131,16 @@
hgId.set<HapticGenerator::Id::commonTag>(tag);
id.set<Parameter::Id::hapticGeneratorTag>(hgId);
EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
- EXPECT_EQ(expectParam, getParam);
+ EXPECT_EQ(expectParam, getParam) << expectParam.toString() << "\n"
+ << getParam.toString();
}
}
void addHapticScaleParam(int id, HapticGenerator::VibratorScale scale) {
- HapticGenerator hg;
- HapticGenerator::HapticScale hapticScale = {.id = id, .scale = scale};
- hg.set<HapticGenerator::hapticScale>(hapticScale);
- mTags.push_back({HapticGenerator::hapticScale, hg});
+ HapticGenerator setHg;
+ std::vector<HapticGenerator::HapticScale> hapticScales = {{.id = id, .scale = scale}};
+ setHg.set<HapticGenerator::hapticScales>(hapticScales);
+ mTags.push_back({HapticGenerator::hapticScales, setHg});
}
void addVibratorInformationParam(float resonantFrequencyHz, float qFactor, float maxAmplitude) {
@@ -161,7 +154,8 @@
}
private:
- std::vector<std::pair<HapticGenerator::Tag, HapticGenerator>> mTags;
+ enum ParamTestEnum { PARAM_TEST_TAG, PARAM_TEST_TARGET };
+ std::vector<std::tuple<HapticGenerator::Tag, HapticGenerator>> mTags;
void CleanUp() { mTags.clear(); }
};
@@ -171,6 +165,12 @@
SetAndGetHapticGeneratorParameters();
}
+TEST_P(HapticGeneratorParamTest, SetAndGetMultipleHapticScales) {
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(mParamHapticScaleId, mParamVibratorScale));
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(mParamHapticScaleId, mParamVibratorScale));
+ SetAndGetHapticGeneratorParameters();
+}
+
TEST_P(HapticGeneratorParamTest, SetAndGetVibratorInformation) {
EXPECT_NO_FATAL_FAILURE(addVibratorInformationParam(mParamResonantFrequency, mParamQFactor,
mParamMaxAmplitude));
@@ -212,7 +212,7 @@
::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
IFactory::descriptor, kHapticGeneratorTypeUUID)),
testing::Values(MIN_ID - 1),
- testing::Values(HapticGenerator::VibratorScale::MUTE),
+ testing::Values(HapticGenerator::VibratorScale::NONE),
testing::Values(MIN_FLOAT), testing::Values(MIN_FLOAT),
testing::Values(MIN_FLOAT)),
[](const testing::TestParamInfo<HapticGeneratorParamTest::ParamType>& info) {
@@ -236,9 +236,202 @@
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
return name;
});
-
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorParamTest);
+// Test HapticScale[] hapticScales parameter
+using HapticGeneratorScalesTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>>;
+class HapticGeneratorScalesTest : public ::testing::TestWithParam<HapticGeneratorScalesTestParam>,
+ public EffectHelper {
+ public:
+ HapticGeneratorScalesTest() {
+ std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
+ }
+
+ void SetUp() override {
+ ASSERT_NE(nullptr, mFactory);
+ ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+
+ Parameter::Common common = EffectHelper::createParamCommon(
+ 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
+ kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
+ IEffect::OpenEffectReturn ret;
+ ASSERT_NO_FATAL_FAILURE(open(mEffect, common, std::nullopt, &ret, EX_NONE));
+ ASSERT_NE(nullptr, mEffect);
+ }
+
+ void TearDown() override {
+ ASSERT_NO_FATAL_FAILURE(close(mEffect));
+ ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+ CleanUp();
+ }
+
+ static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
+ std::shared_ptr<IFactory> mFactory;
+ std::shared_ptr<IEffect> mEffect;
+ Descriptor mDescriptor;
+
+ void addHapticScaleParam(std::vector<HapticGenerator::HapticScale> scales) {
+ mHapticScales.push_back(HapticGenerator::make<HapticGenerator::hapticScales>(scales));
+ for (const auto& scale : scales) {
+ expectMap.insert_or_assign(scale.id, scale.scale);
+ }
+ }
+
+ void SetHapticScaleParameters() {
+ // std::unordered_set<HapticGenerator::HapticScale> target;
+ for (auto& it : mHapticScales) {
+ Parameter::Specific specific =
+ Parameter::Specific::make<Parameter::Specific::hapticGenerator>(it);
+ Parameter param = Parameter::make<Parameter::specific>(specific);
+ EXPECT_STATUS(EX_NONE, mEffect->setParameter(param)) << param.toString();
+ }
+ }
+
+ void checkHapticScaleParameter() {
+ // get parameter
+ Parameter targetParam;
+ HapticGenerator::Id hgId = HapticGenerator::Id::make<HapticGenerator::Id::commonTag>(
+ HapticGenerator::hapticScales);
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::hapticGeneratorTag>(hgId);
+ EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &targetParam));
+ ASSERT_EQ(Parameter::specific, targetParam.getTag());
+ Parameter::Specific specific = targetParam.get<Parameter::specific>();
+ ASSERT_EQ(Parameter::Specific::hapticGenerator, specific.getTag());
+ HapticGenerator hg = specific.get<Parameter::Specific::hapticGenerator>();
+ ASSERT_EQ(HapticGenerator::hapticScales, hg.getTag());
+ std::vector<HapticGenerator::HapticScale> scales = hg.get<HapticGenerator::hapticScales>();
+ ASSERT_EQ(scales.size(), expectMap.size());
+ for (const auto& scale : scales) {
+ auto itor = expectMap.find(scale.id);
+ ASSERT_NE(expectMap.end(), itor);
+ ASSERT_EQ(scale.scale, itor->second);
+ expectMap.erase(scale.id);
+ }
+ ASSERT_EQ(0ul, expectMap.size());
+ }
+
+ const static HapticGenerator::HapticScale kHapticScaleWithMinId;
+ const static HapticGenerator::HapticScale kHapticScaleWithMinIdNew;
+ const static HapticGenerator::HapticScale kHapticScale;
+ const static HapticGenerator::HapticScale kHapticScaleNew;
+ const static HapticGenerator::HapticScale kHapticScaleWithMaxId;
+ const static HapticGenerator::HapticScale kHapticScaleWithMaxIdNew;
+
+ std::vector<HapticGenerator> mHapticScales;
+
+ void CleanUp() {
+ mHapticScales.clear();
+ expectMap.clear();
+ }
+
+ private:
+ std::map<int /* trackID */, HapticGenerator::VibratorScale> expectMap;
+};
+
+const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMinId = {
+ .id = MIN_ID, .scale = HapticGenerator::VibratorScale::MUTE};
+const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMinIdNew = {
+ .id = MIN_ID, .scale = HapticGenerator::VibratorScale::VERY_LOW};
+const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScale = {
+ .id = 1, .scale = HapticGenerator::VibratorScale::LOW};
+const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleNew = {
+ .id = 1, .scale = HapticGenerator::VibratorScale::NONE};
+const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMaxId = {
+ .id = MAX_ID, .scale = HapticGenerator::VibratorScale::VERY_HIGH};
+const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMaxIdNew = {
+ .id = MAX_ID, .scale = HapticGenerator::VibratorScale::MUTE};
+
+TEST_P(HapticGeneratorScalesTest, SetAndUpdateOne) {
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleNew}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMinIdNew}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxIdNew}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+
+ EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
+}
+
+TEST_P(HapticGeneratorScalesTest, SetAndUpdateVector) {
+ EXPECT_NO_FATAL_FAILURE(
+ addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(
+ {kHapticScaleNew, kHapticScaleWithMaxIdNew, kHapticScaleWithMinIdNew}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+
+ EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
+}
+
+TEST_P(HapticGeneratorScalesTest, SetAndUpdateMultipleVector) {
+ EXPECT_NO_FATAL_FAILURE(
+ addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(
+ {kHapticScaleNew, kHapticScaleWithMaxIdNew, kHapticScaleWithMinIdNew}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(
+ addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+
+ EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
+}
+
+TEST_P(HapticGeneratorScalesTest, SetOneAndAddMoreVector) {
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId, kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+
+ EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
+}
+
+TEST_P(HapticGeneratorScalesTest, SetMultipleAndAddOneVector) {
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId, kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+
+ EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
+}
+
+TEST_P(HapticGeneratorScalesTest, SetMultipleVectorRepeat) {
+ EXPECT_NO_FATAL_FAILURE(
+ addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(
+ addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+ EXPECT_NO_FATAL_FAILURE(
+ addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
+ EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
+
+ EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ HapticGeneratorScalesTest, HapticGeneratorScalesTest,
+ ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+ IFactory::descriptor, kHapticGeneratorTypeUUID))),
+ [](const testing::TestParamInfo<HapticGeneratorScalesTest::ParamType>& info) {
+ auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
+ std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
+ descriptor.common.name + "_UUID_" +
+ descriptor.common.id.uuid.toString();
+ std::replace_if(
+ name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+ return name;
+ });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorScalesTest);
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index e7bb282..3f0e2d2 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -765,4 +765,12 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="native">
+ <name>mapper</name>
+ <version>1.0</version>
+ <interface>
+ <name>I</name>
+ <regex-instance>.*</regex-instance>
+ </interface>
+ </hal>
</compatibility-matrix>
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
index e7f5a0f..88badc7 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -57,6 +57,7 @@
srcs: [
"AttestKeyTest.cpp",
"DeviceUniqueAttestationTest.cpp",
+ "KeyBlobUpgradeTest.cpp",
"KeyMintTest.cpp",
"SecureElementProvisioningTest.cpp",
],
diff --git a/security/keymint/aidl/vts/functional/KeyBlobUpgradeTest.cpp b/security/keymint/aidl/vts/functional/KeyBlobUpgradeTest.cpp
new file mode 100644
index 0000000..c952012
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/KeyBlobUpgradeTest.cpp
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// The tests in this file are intended to be run manually, to allow testing of whether
+// keyblob upgrade works correctly. The manual procedure is roughly:
+//
+// 1) Run the "*Before*" subset of these tests with the `--keyblob_dir <dir>` command-line argument
+// so that keyblobs are saved to a directory on the device:
+//
+// VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest*Before*" \
+// --keyblob_dir /data/local/tmp/keymint-blobs
+//
+// All tests should pass, and the `UpgradeKeyBlobs` test should indicate that no keyblob
+// upgrades were needed.
+//
+// 2) Copy the generated keyblobs off the device into a safe place.
+//
+// adb pull /data/local/tmp/keymint-blobs
+//
+// 3) Upgrade the device to a new version.
+//
+// 4) Push the saved keyblobs back onto the upgraded device.
+//
+// adb push keymint-blobs /data/local/tmp/keymint-blobs
+//
+// 5) Run the "*After*" subset of these tests with the `--keyblob_dir <dir>` command-line argument
+// pointing to the directory with the keyblobs:
+//
+// VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest*After*" \
+// --keyblob_dir /data/local/tmp/keymint-blobs
+//
+// (Note that this skips the `CreateKeyBlobs` test, which would otherwise replace the saved
+// keyblobs with freshly generated ones.).
+//
+// All tests should pass, and the `UpgradeKeyBlobs` test should have output that matches whether
+// upgrade was expected or not.
+
+#define LOG_TAG "keymint_1_test"
+#include <cutils/log.h>
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+
+#include <unistd.h>
+
+#include <openssl/curve25519.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/x509v3.h>
+
+#include "KeyMintAidlTestBase.h"
+
+using aidl::android::hardware::security::keymint::KeyCharacteristics;
+
+namespace aidl::android::hardware::security::keymint::test {
+
+namespace {
+
+std::vector<std::string> keyblob_names_tee = {
+ "aes-key", "aes-key-rr", "des-key", "hmac-key",
+ "rsa-key", "p256-key", "ed25519-key", "x25519-key",
+ "rsa-attest-key", "p256-attest-key", "ed25519-attest-key"};
+
+std::vector<std::string> keyblob_names_sb = {"aes-key", "aes-key-rr", "des-key",
+ "hmac-key", "rsa-key", "p256-key",
+ "rsa-attest-key", "p256-attest-key"};
+
+const std::vector<std::string>& keyblob_names(SecurityLevel sec_level) {
+ if (sec_level == SecurityLevel::STRONGBOX) {
+ return keyblob_names_sb;
+ } else {
+ return keyblob_names_tee;
+ }
+}
+
+bool requires_rr(const std::string& name) {
+ return name.find("-rr") != std::string::npos;
+}
+
+bool is_asymmetric(const std::string& name) {
+ return (name.find("rsa") != std::string::npos || name.find("25519") != std::string::npos ||
+ name.find("p256") != std::string::npos);
+}
+
+std::string keyblob_subdir(const std::string& keyblob_dir, const std::string& full_name,
+ bool create) {
+ if (keyblob_dir.empty()) {
+ return "";
+ }
+
+ // Use a subdirectory for the specific instance, so two different KeyMint instances won't
+ // clash with each other.
+ size_t found = full_name.find_last_of('/');
+ std::string subdir = keyblob_dir + "/" + full_name.substr(found + 1);
+
+ if (create) {
+ mkdir(keyblob_dir.c_str(), 0777);
+ mkdir(subdir.c_str(), 0777);
+ }
+ return subdir;
+}
+
+void save_keyblob(const std::string& subdir, const std::string& name,
+ const vector<uint8_t>& keyblob,
+ const std::vector<KeyCharacteristics>& key_characteristics) {
+ // Write the keyblob out to a file.
+ std::string blobname(subdir + "/" + name + ".keyblob");
+ std::ofstream blobfile(blobname, std::ios::out | std::ios::trunc | std::ios::binary);
+ blobfile.write(reinterpret_cast<const char*>(keyblob.data()), keyblob.size());
+ blobfile.close();
+
+ // Dump the characteristics too.
+ std::string charsname(subdir + "/" + name + ".chars");
+ std::ofstream charsfile(charsname, std::ios::out | std::ios::trunc);
+ charsfile << "{\n";
+ for (const auto& characteristic : key_characteristics) {
+ charsfile << " " << characteristic.toString() << "\n";
+ }
+ charsfile << "}\n";
+ charsfile.close();
+
+ // Also write out a hexdump of the keyblob for convenience.
+ std::string hexname(subdir + "/" + name + ".hex");
+ std::ofstream hexfile(hexname, std::ios::out | std::ios::trunc);
+ hexfile << bin2hex(keyblob) << "\n";
+ hexfile.close();
+}
+
+void save_keyblob_and_cert(const std::string& subdir, const std::string& name,
+ const vector<uint8_t>& keyblob,
+ const std::vector<KeyCharacteristics>& key_characteristics,
+ const std::vector<Certificate>& cert_chain) {
+ save_keyblob(subdir, name, keyblob, key_characteristics);
+
+ if (is_asymmetric(name)) {
+ // Dump the leaf certificate as DER.
+ if (cert_chain.empty()) {
+ FAIL() << "No cert available for " << name;
+ } else {
+ const vector<uint8_t>& certdata = cert_chain[0].encodedCertificate;
+ std::string certname(subdir + "/" + name + ".cert");
+ std::ofstream certfile(certname, std::ios::out | std::ios::trunc | std::ios::binary);
+ certfile.write(reinterpret_cast<const char*>(certdata.data()), certdata.size());
+ certfile.close();
+ }
+ }
+}
+
+void delete_keyblob(const std::string& subdir, const std::string& name) {
+ std::string blobname(subdir + "/" + name + ".keyblob");
+ unlink(blobname.c_str());
+ std::string charsname(subdir + "/" + name + ".chars");
+ unlink(charsname.c_str());
+ std::string hexname(subdir + "/" + name + ".hex");
+ unlink(hexname.c_str());
+ std::string certname(subdir + "/" + name + ".cert");
+ unlink(certname.c_str());
+}
+
+std::vector<uint8_t> load_file(const std::string& subdir, const std::string& name,
+ const std::string& suffix) {
+ std::string blobname(subdir + "/" + name + suffix);
+ std::ifstream blobfile(blobname, std::ios::in | std::ios::binary);
+
+ std::vector<uint8_t> data((std::istreambuf_iterator<char>(blobfile)),
+ std::istreambuf_iterator<char>());
+ return data;
+}
+
+std::vector<uint8_t> load_keyblob(const std::string& subdir, const std::string& name) {
+ return load_file(subdir, name, ".keyblob");
+}
+
+std::vector<uint8_t> load_cert(const std::string& subdir, const std::string& name) {
+ return load_file(subdir, name, ".cert");
+}
+
+} // namespace
+
+class KeyBlobUpgradeTest : public KeyMintAidlTestBase {
+ protected:
+ void UpgradeKeyBlobs(bool expectUpgrade) {
+ std::string subdir = keyblob_subdir(keyblob_dir, GetParam(), /* create? */ false);
+ if (subdir.empty()) {
+ GTEST_SKIP() << "No keyblob directory provided";
+ }
+
+ for (std::string name : keyblob_names(SecLevel())) {
+ for (bool with_hidden : {false, true}) {
+ std::string app_id;
+ std::string app_data;
+ auto builder = AuthorizationSetBuilder();
+ if (with_hidden) {
+ // Build a variant keyblob that requires app_id/app_data
+ app_id = "appid";
+ app_data = "appdata";
+ builder.Authorization(TAG_APPLICATION_ID, "appid")
+ .Authorization(TAG_APPLICATION_DATA, "appdata");
+ name += "-hidden";
+ }
+ SCOPED_TRACE(testing::Message() << name);
+
+ // Load the old format keyblob.
+ std::vector<uint8_t> keyblob = load_keyblob(subdir, name);
+ if (keyblob.empty()) {
+ if (requires_rr(name)) {
+ std::cerr << "Skipping missing keyblob file '" << name
+ << "', assuming rollback resistance unavailable\n";
+ } else {
+ FAIL() << "Missing keyblob file '" << name << "'";
+ }
+ continue;
+ }
+
+ // An upgrade will either produce a new keyblob or no data (if upgrade isn't
+ // needed).
+ std::vector<uint8_t> upgraded_keyblob;
+ Status result =
+ keymint_->upgradeKey(keyblob, builder.vector_data(), &upgraded_keyblob);
+ ASSERT_EQ(ErrorCode::OK, GetReturnErrorCode(result));
+
+ if (upgraded_keyblob.empty()) {
+ std::cerr << "Keyblob '" << name << "' did not require upgrade\n";
+ EXPECT_TRUE(!expectUpgrade) << "Keyblob '" << name << "' unexpectedly upgraded";
+ } else {
+ // Ensure the old format keyblob is deleted (so any secure deletion data is
+ // cleaned up).
+ EXPECT_EQ(ErrorCode::OK, DeleteKey(&keyblob));
+
+ std::vector<uint8_t> app_id_v(app_id.begin(), app_id.end());
+ std::vector<uint8_t> app_data_v(app_data.begin(), app_data.end());
+ std::vector<KeyCharacteristics> key_characteristics;
+ result = keymint_->getKeyCharacteristics(upgraded_keyblob, app_id_v, app_data_v,
+ &key_characteristics);
+ ASSERT_EQ(ErrorCode::OK, GetReturnErrorCode(result))
+ << "Failed getKeyCharacteristics() after upgrade";
+
+ save_keyblob(subdir, name, upgraded_keyblob, key_characteristics);
+ // Cert file is left unchanged.
+ std::cerr << "Keyblob '" << name << "' upgraded\n";
+ EXPECT_TRUE(expectUpgrade)
+ << "Keyblob '" << name << "' unexpectedly left as-is";
+ }
+ }
+ }
+ }
+};
+
+// To save off keyblobs before upgrade, use:
+//
+// VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest.CreateKeyBlobs*" \
+// --keyblob_dir /data/local/tmp/keymint-blobs
+//
+// Then copy the contents of the /data/local/tmp/keymint-blobs/ directory somewhere safe:
+//
+// adb pull /data/local/tmp/keymint-blobs/
+TEST_P(KeyBlobUpgradeTest, CreateKeyBlobsBefore) {
+ std::string subdir = keyblob_subdir(keyblob_dir, GetParam(), /* create? */ true);
+
+ std::map<const std::string, AuthorizationSetBuilder> keys_info = {
+ {"aes-key", AuthorizationSetBuilder()
+ .AesEncryptionKey(256)
+ .BlockMode(BlockMode::ECB)
+ .Padding(PaddingMode::PKCS7)
+ .Authorization(TAG_NO_AUTH_REQUIRED)},
+ {"aes-key-rr", AuthorizationSetBuilder()
+ .AesEncryptionKey(256)
+ .BlockMode(BlockMode::ECB)
+ .Padding(PaddingMode::PKCS7)
+ .Authorization(TAG_ROLLBACK_RESISTANCE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)},
+ {"des-key", AuthorizationSetBuilder()
+ .TripleDesEncryptionKey(168)
+ .BlockMode(BlockMode::ECB)
+ .Padding(PaddingMode::PKCS7)
+ .Authorization(TAG_NO_AUTH_REQUIRED)},
+ {"hmac-key", AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(Digest::SHA1)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)
+ .Authorization(TAG_NO_AUTH_REQUIRED)},
+ {"rsa-key", AuthorizationSetBuilder()
+ .RsaEncryptionKey(2048, 65537)
+ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+ .Digest(Digest::NONE)
+ .Digest(Digest::SHA1)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity()},
+ {
+ "p256-key",
+ AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .Digest(Digest::NONE)
+ .Digest(Digest::SHA1)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ },
+ {
+ "ed25519-key",
+ AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::CURVE_25519)
+ .Digest(Digest::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ },
+ {"x25519-key", AuthorizationSetBuilder()
+ .Authorization(TAG_EC_CURVE, EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .Authorization(TAG_ALGORITHM, Algorithm::EC)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity()},
+ {"rsa-attest-key", AuthorizationSetBuilder()
+ .RsaKey(2048, 65537)
+ .AttestKey()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity()},
+ {
+ "p256-attest-key",
+ AuthorizationSetBuilder()
+ .EcdsaKey(EcCurve::P_256)
+ .AttestKey()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ },
+ {
+ "ed25519-attest-key",
+ AuthorizationSetBuilder()
+ .EcdsaKey(EcCurve::CURVE_25519)
+ .AttestKey()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ }};
+
+ for (std::string name : keyblob_names(SecLevel())) {
+ auto entry = keys_info.find(name);
+ ASSERT_NE(entry, keys_info.end()) << "no builder for " << name;
+ auto builder = entry->second;
+ for (bool with_hidden : {false, true}) {
+ if (with_hidden) {
+ // Build a variant keyblob that requires app_id/app_data
+ builder.Authorization(TAG_APPLICATION_ID, "appid")
+ .Authorization(TAG_APPLICATION_DATA, "appdata");
+ name += "-hidden";
+ }
+ SCOPED_TRACE(testing::Message() << name);
+
+ vector<uint8_t> keyblob;
+ vector<KeyCharacteristics> key_characteristics;
+ vector<Certificate> cert_chain;
+ auto result =
+ GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain);
+
+ if (requires_rr(name) && result == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) {
+ // Rollback resistance support is optional.
+ std::cerr << "Skipping '" << name << "' key as rollback resistance unavailable\n";
+ continue;
+ }
+ ASSERT_EQ(ErrorCode::OK, result) << " failed for " << name;
+
+ if (!subdir.empty()) {
+ save_keyblob_and_cert(subdir, name, keyblob, key_characteristics, cert_chain);
+ }
+ }
+ }
+
+ if (!subdir.empty()) {
+ std::cerr << "Save generated keyblobs with:\n\n adb pull " << keyblob_dir << "\n\n";
+ }
+}
+
+TEST_P(KeyBlobUpgradeTest, UpgradeKeyBlobsBefore) {
+ // Check that attempting to upgrade valid keyblobs does nothing.
+ UpgradeKeyBlobs(/* expectUpgrade= */ false);
+}
+
+// To run this test:
+//
+// - save off some keyblobs before upgrade as per the CreateKeyBlobs test above.
+// - upgrade the device to a version that should trigger keyblob upgrade (e.g. different patchlevel)
+// - put the saved keyblobs back onto the upgraded device:
+//
+// adb push keymint-blobs /data/local/tmp/keymint-blobs
+//
+// - run the test with:
+//
+// VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest.UpgradeKeyBlobsAfter*" \
+// --keyblob_dir /data/local/tmp/keymint-blobs
+//
+// - this replaces the keyblob contents in that directory; if needed, save the upgraded keyblobs
+// with:
+// adb pull /data/local/tmp/keymint-blobs/
+TEST_P(KeyBlobUpgradeTest, UpgradeKeyBlobsAfter) {
+ UpgradeKeyBlobs(/* expectUpgrade= */ true);
+}
+
+// To run this test:
+//
+// - save off some keyblobs before upgrade as per the CreateKeyBlobs test above
+// - if needed, upgrade the saved keyblobs as per the UpgradeKeyBlobs test above
+// - run the test with:
+//
+// VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest.UseKeyBlobs*" \
+// --keyblob_dir /data/local/tmp/keymint-blobs
+TEST_P(KeyBlobUpgradeTest, UseKeyBlobsBeforeOrAfter) {
+ std::string subdir = keyblob_subdir(keyblob_dir, GetParam(), /* create? */ false);
+ if (subdir.empty()) {
+ GTEST_SKIP() << "No keyblob directory provided with (e.g.) --keyblob_dir "
+ "/data/local/tmp/keymint-blobs";
+ }
+
+ for (std::string name : keyblob_names(SecLevel())) {
+ for (bool with_hidden : {false, true}) {
+ auto builder = AuthorizationSetBuilder();
+ if (with_hidden) {
+ // Build a variant keyblob that requires app_id/app_data
+ builder.Authorization(TAG_APPLICATION_ID, "appid")
+ .Authorization(TAG_APPLICATION_DATA, "appdata");
+ name += "-hidden";
+ }
+ SCOPED_TRACE(testing::Message() << name);
+ std::vector<uint8_t> keyblob = load_keyblob(subdir, name);
+ if (keyblob.empty()) {
+ if (requires_rr(name)) {
+ std::cerr << "Skipping missing keyblob file '" << name
+ << "', assuming rollback resistance unavailable\n";
+ } else {
+ FAIL() << "Missing keyblob file '" << name << "'";
+ }
+ continue;
+ }
+
+ std::vector<uint8_t> cert;
+ if (is_asymmetric(name)) {
+ cert = load_cert(subdir, name);
+ }
+
+ // Perform an algorithm-specific operation with the keyblob.
+ string message = "Hello World!";
+ AuthorizationSet out_params;
+ if (name.find("aes-key") != std::string::npos) {
+ builder.BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+ string ciphertext = EncryptMessage(keyblob, message, builder, &out_params);
+ string plaintext = DecryptMessage(keyblob, ciphertext, builder);
+ EXPECT_EQ(message, plaintext);
+ } else if (name.find("des-key") != std::string::npos) {
+ builder.BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+ string ciphertext = EncryptMessage(keyblob, message, builder, &out_params);
+ string plaintext = DecryptMessage(keyblob, ciphertext, builder);
+ EXPECT_EQ(message, plaintext);
+ } else if (name.find("hmac-key") != std::string::npos) {
+ builder.Digest(Digest::SHA1);
+ auto sign_builder = builder;
+ sign_builder.Authorization(TAG_MAC_LENGTH, 128);
+ string tag = SignMessage(keyblob, message, sign_builder);
+ VerifyMessage(keyblob, message, tag, builder);
+ } else if (name.find("rsa-key") != std::string::npos) {
+ builder.Digest(Digest::NONE).Padding(PaddingMode::NONE);
+ string signature = SignMessage(keyblob, message, builder);
+ LocalVerifyMessage(cert, message, signature, builder);
+ } else if (name.find("p256-key") != std::string::npos) {
+ builder.Digest(Digest::SHA1);
+ string signature = SignMessage(keyblob, message, builder);
+ LocalVerifyMessage(cert, message, signature, builder);
+ } else if (name.find("ed25519-key") != std::string::npos) {
+ builder.Digest(Digest::NONE);
+ string signature = SignMessage(keyblob, message, builder);
+ LocalVerifyMessage(cert, message, signature, builder);
+ } else if (name.find("x25519-key") != std::string::npos) {
+ // Generate EC key on same curve locally (with access to private key material).
+ uint8_t localPrivKeyData[32];
+ uint8_t localPubKeyData[32];
+ X25519_keypair(localPubKeyData, localPrivKeyData);
+ EVP_PKEY_Ptr localPrivKey(EVP_PKEY_new_raw_private_key(
+ EVP_PKEY_X25519, nullptr, localPrivKeyData, sizeof(localPrivKeyData)));
+ // Get encoded form of the public part of the locally generated key.
+ unsigned char* p = nullptr;
+ int localPublicKeySize = i2d_PUBKEY(localPrivKey.get(), &p);
+ ASSERT_GT(localPublicKeySize, 0);
+ vector<uint8_t> localPublicKey(
+ reinterpret_cast<const uint8_t*>(p),
+ reinterpret_cast<const uint8_t*>(p + localPublicKeySize));
+ OPENSSL_free(p);
+
+ // Agree on a key between local and KeyMint.
+ string data;
+ ASSERT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::AGREE_KEY, keyblob, builder, &out_params));
+ ASSERT_EQ(ErrorCode::OK,
+ Finish(string(localPublicKey.begin(), localPublicKey.end()), &data));
+ vector<uint8_t> keymint_data(data.begin(), data.end());
+
+ // Extract the public key for the KeyMint key from the cert.
+ X509_Ptr kmKeyCert(parse_cert_blob(cert));
+ ASSERT_NE(kmKeyCert, nullptr);
+ EVP_PKEY_Ptr kmPubKey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
+ ASSERT_NE(kmPubKey.get(), nullptr);
+
+ size_t kmPubKeySize = 32;
+ uint8_t kmPubKeyData[32];
+ ASSERT_EQ(1,
+ EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+ ASSERT_EQ(kmPubKeySize, 32);
+
+ // Agree on a key between KeyMint and local.
+ uint8_t sharedKey[32];
+ ASSERT_EQ(1, X25519(sharedKey, localPrivKeyData, kmPubKeyData));
+ vector<uint8_t> local_data(sharedKey, sharedKey + 32);
+
+ // Both ways round should agree.
+ EXPECT_EQ(keymint_data, local_data);
+ } else if (name.find("-attest-key") != std::string::npos) {
+ // Covers rsa-attest-key, p256-attest-key, ed25519-attest-key.
+
+ // Use attestation key to sign RSA signing key
+ AttestationKey attest_key;
+ attest_key.keyBlob = keyblob;
+ attest_key.attestKeyParams = builder.vector_data();
+ attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+ vector<uint8_t> attested_key_blob;
+ vector<KeyCharacteristics> attested_key_characteristics;
+ vector<Certificate> attested_key_cert_chain;
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("challenge")
+ .AttestationApplicationId("app-id")
+ .SetDefaultValidity(),
+ attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+ CheckedDeleteKey(&attested_key_blob);
+ } else {
+ FAIL() << "Unexpected name: " << name;
+ }
+ }
+ }
+}
+
+// This test target deletes any keys from the keyblob subdirectory that have rollback resistance
+// enabled.
+TEST_P(KeyBlobUpgradeTest, DeleteRRKeyBlobsAfter) {
+ std::string subdir = keyblob_subdir(keyblob_dir, GetParam(), /* create? */ false);
+ if (subdir.empty()) {
+ GTEST_SKIP() << "No keyblob directory provided with (e.g.) --keyblob_dir "
+ "/data/local/tmp/keymint-blobs";
+ }
+
+ for (std::string name : keyblob_names(SecLevel())) {
+ for (bool with_hidden : {false, true}) {
+ auto builder = AuthorizationSetBuilder();
+ if (with_hidden) {
+ // Build a variant keyblob that requires app_id/app_data
+ builder.Authorization(TAG_APPLICATION_ID, "appid")
+ .Authorization(TAG_APPLICATION_DATA, "appdata");
+ name += "-hidden";
+ }
+ if (!requires_rr(name)) {
+ std::cerr << "Skipping keyblob file '" << name
+ << "' which does not use rollback resistance\n";
+ continue;
+ }
+ SCOPED_TRACE(testing::Message() << name);
+ std::vector<uint8_t> keyblob = load_keyblob(subdir, name);
+ if (keyblob.empty()) {
+ std::cerr << "Skipping missing keyblob file '" << name
+ << "', assuming rollback resistance unavailable\n";
+ continue;
+ }
+
+ // Delete the key
+ ASSERT_EQ(ErrorCode::OK, DeleteKey(&keyblob));
+
+ // Remove all files relating to the deleted key.
+ std::cerr << "Deleting files for deleted key '" << name << ";";
+ delete_keyblob(subdir, name);
+
+ // Attempting to use the keyblob after deletion should fail.
+ AuthorizationSet out_params;
+ if (name.find("aes-key") != std::string::npos) {
+ builder.BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+ EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+ Begin(KeyPurpose::ENCRYPT, keyblob, builder, &out_params));
+ } else {
+ FAIL() << "Unexpected name: " << name;
+ }
+ }
+ }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(KeyBlobUpgradeTest);
+
+} // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 43ad30a..6c012fa 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -184,6 +184,7 @@
bool KeyMintAidlTestBase::arm_deleteAllKeys = false;
bool KeyMintAidlTestBase::dump_Attestations = false;
+std::string KeyMintAidlTestBase::keyblob_dir;
uint32_t KeyMintAidlTestBase::boot_patch_level(
const vector<KeyCharacteristics>& key_characteristics) {
@@ -946,9 +947,15 @@
const AuthorizationSet& params) {
SCOPED_TRACE("LocalVerifyMessage");
- // Retrieve the public key from the leaf certificate.
ASSERT_GT(cert_chain_.size(), 0);
- X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ LocalVerifyMessage(cert_chain_[0].encodedCertificate, message, signature, params);
+}
+
+void KeyMintAidlTestBase::LocalVerifyMessage(const vector<uint8_t>& der_cert, const string& message,
+ const string& signature,
+ const AuthorizationSet& params) {
+ // Retrieve the public key from the leaf certificate.
+ X509_Ptr key_cert(parse_cert_blob(der_cert));
ASSERT_TRUE(key_cert.get());
EVP_PKEY_Ptr pub_key(X509_get_pubkey(key_cert.get()));
ASSERT_TRUE(pub_key.get());
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 5b09ca5..908eeab 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -64,6 +64,10 @@
static bool arm_deleteAllKeys;
static bool dump_Attestations;
+ // Directory to store/retrieve keyblobs, using subdirectories named for the
+ // KeyMint instance in question (e.g. "./default/", "./strongbox/").
+ static std::string keyblob_dir;
+
void SetUp() override;
void TearDown() override {
if (key_blob_.size()) {
@@ -206,6 +210,8 @@
const string& signature, const AuthorizationSet& params);
void VerifyMessage(const string& message, const string& signature,
const AuthorizationSet& params);
+ void LocalVerifyMessage(const vector<uint8_t>& der_cert, const string& message,
+ const string& signature, const AuthorizationSet& params);
void LocalVerifyMessage(const string& message, const string& signature,
const AuthorizationSet& params);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index ca18082..342b926 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -8649,6 +8649,15 @@
// interactions.
aidl::android::hardware::security::keymint::test::check_boot_pl = false;
}
+ if (std::string(argv[i]) == "--keyblob_dir") {
+ if (i + 1 >= argc) {
+ std::cerr << "Missing argument for --keyblob_dir\n";
+ return 1;
+ }
+ aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::keyblob_dir =
+ std::string(argv[i + 1]);
+ ++i;
+ }
}
}
return RUN_ALL_TESTS();