Merge "Refactor Tuner 1.0 vts to use dynamic configuration" into sc-dev
diff --git a/audio/7.0/config/api/current.txt b/audio/7.0/config/api/current.txt
index 653531d..7d2706c 100644
--- a/audio/7.0/config/api/current.txt
+++ b/audio/7.0/config/api/current.txt
@@ -155,6 +155,12 @@
enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADSET;
}
+ public enum AudioEncapsulationType {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.audio.policy.configuration.V7_0.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_IEC61937;
+ enum_constant public static final android.audio.policy.configuration.V7_0.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_NONE;
+ }
+
public enum AudioFormat {
method @NonNull public String getRawName();
enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC;
@@ -488,10 +494,12 @@
public class Profile {
ctor public Profile();
method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioChannelMask> getChannelMasks();
+ method @Nullable public android.audio.policy.configuration.V7_0.AudioEncapsulationType getEncapsulationType();
method @Nullable public String getFormat();
method @Nullable public String getName();
method @Nullable public java.util.List<java.math.BigInteger> getSamplingRates();
method public void setChannelMasks(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioChannelMask>);
+ method public void setEncapsulationType(@Nullable android.audio.policy.configuration.V7_0.AudioEncapsulationType);
method public void setFormat(@Nullable String);
method public void setName(@Nullable String);
method public void setSamplingRates(@Nullable java.util.List<java.math.BigInteger>);
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
index ee51aa8..2030921 100644
--- a/audio/7.0/config/audio_policy_configuration.xsd
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -551,11 +551,18 @@
<xs:simpleType name="channelMasks">
<xs:list itemType="audioChannelMask" />
</xs:simpleType>
+ <xs:simpleType name="audioEncapsulationType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_NONE"/>
+ <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_IEC61937"/>
+ </xs:restriction>
+ </xs:simpleType>
<xs:complexType name="profile">
<xs:attribute name="name" type="xs:token" use="optional"/>
<xs:attribute name="format" type="extendableAudioFormat" use="optional"/>
<xs:attribute name="samplingRates" type="samplingRates" use="optional"/>
<xs:attribute name="channelMasks" type="channelMasks" use="optional"/>
+ <xs:attribute name="encapsulationType" type="audioEncapsulationType" use="optional"/>
</xs:complexType>
<xs:simpleType name="audioGainMode">
<xs:restriction base="xs:string">
diff --git a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
index 88dd12e..7d83556 100644
--- a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
+++ b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
@@ -287,6 +287,10 @@
return isLinearPcm(stringToAudioFormat(format));
}
+static inline bool isUnknownAudioEncapsulationType(const std::string& encapsulationType) {
+ return stringToAudioEncapsulationType(encapsulationType) == AudioEncapsulationType::UNKNOWN;
+}
+
} // namespace android::audio::policy::configuration::V7_0
#endif // ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
diff --git a/audio/common/7.0/types.hal b/audio/common/7.0/types.hal
index 4f920e4..6fca93e 100644
--- a/audio/common/7.0/types.hal
+++ b/audio/common/7.0/types.hal
@@ -145,6 +145,14 @@
};
/**
+ * Audio encapsulation type indicates the encapsulation type that is required
+ * for playback/capture.
+ * See 'audioEncapsulationType' in audio_policy_configuration.xsd for the list
+ * of allowed values.
+ */
+typedef string AudioEncapsulationType;
+
+/**
* Configurations supported for a certain audio format.
*/
struct AudioProfile {
@@ -156,6 +164,35 @@
};
/**
+ * AudioTransport struct describes the capability of an audio port. The
+ * capability is described via AudioProfile or raw hardware descriptors for
+ * for formats that are not supported by the platform.
+ */
+struct AudioTransport {
+ safe_union AudioCapability {
+ /**
+ * A certain audio format that is known by the platform and its
+ * corresponding configuration.
+ */
+ AudioProfile profile;
+ /**
+ * The audio descriptor that is reported from EDID. See HDMI
+ * specification 1.4b section 7 and CEA-861-G section 7.5.2 for more
+ * information. When this value is set, it indicates the standard is
+ * AUDIO_STANDARD_EDID.
+ */
+ vec<uint8_t> edid;
+ } audioCapability;
+
+ /**
+ * The encapsulation type that is required when the framework is using this
+ * format when playing or capturing to/from a stream or device exposing this
+ * audio transport.
+ */
+ AudioEncapsulationType encapsulationType;
+};
+
+/**
* Major modes for a mobile device. The current mode setting affects audio
* routing.
*/
@@ -488,8 +525,12 @@
* E.g. "telephony_tx" or "fm_tuner".
*/
string name;
- /** List of audio profiles supported by the port. */
- vec<AudioProfile> profiles;
+ /**
+ * List of audio transports supported by the audio port. This includes
+ * supported formats and raw hardware descriptors for formats not supported
+ * by the platform.
+ */
+ vec<AudioTransport> transports;
/** List of gain controls attached to the port. */
vec<AudioGain> gains;
/** Parameters that depend on the actual port role. */
diff --git a/audio/common/all-versions/default/7.0/HidlUtils.cpp b/audio/common/all-versions/default/7.0/HidlUtils.cpp
index 2949fac..5a5b5d2 100644
--- a/audio/common/all-versions/default/7.0/HidlUtils.cpp
+++ b/audio/common/all-versions/default/7.0/HidlUtils.cpp
@@ -715,6 +715,27 @@
return result;
}
+status_t HidlUtils::encapsulationTypeFromHal(audio_encapsulation_type_t halEncapsulationType,
+ AudioEncapsulationType* encapsulationType) {
+ *encapsulationType = audio_encapsulation_type_to_string(halEncapsulationType);
+ if (!encapsulationType->empty() && !xsd::isUnknownAudioEncapsulationType(*encapsulationType)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio encapsulation type value 0x%X", halEncapsulationType);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::encapsulationTypeToHal(const AudioEncapsulationType& encapsulationType,
+ audio_encapsulation_type_t* halEncapsulationType) {
+ if (!xsd::isUnknownAudioEncapsulationType(encapsulationType) &&
+ audio_encapsulation_type_from_string(encapsulationType.c_str(), halEncapsulationType)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio encapsulation type \"%s\"", encapsulationType.c_str());
+ *halEncapsulationType = AUDIO_ENCAPSULATION_TYPE_NONE;
+ return BAD_VALUE;
+}
+
status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
struct audio_port_v7 halPortV7 = {};
audio_populate_audio_port_v7(&halPort, &halPortV7);
@@ -758,11 +779,7 @@
CONVERT_CHECKED(audioPortExtendedInfoFromHal(halPort.role, halPort.type, halDevice, halMix,
halSession, &port->ext, &isInput),
result);
- port->profiles.resize(halPort.num_audio_profiles);
- for (size_t i = 0; i < halPort.num_audio_profiles; ++i) {
- CONVERT_CHECKED(audioProfileFromHal(halPort.audio_profiles[i], isInput, &port->profiles[i]),
- result);
- }
+ CONVERT_CHECKED(audioTransportsFromHal(halPort, isInput, &port->transports), result);
port->gains.resize(halPort.num_gains);
for (size_t i = 0; i < halPort.num_gains; ++i) {
CONVERT_CHECKED(audioGainFromHal(halPort.gains[i], isInput, &port->gains[i]), result);
@@ -780,15 +797,7 @@
ALOGE("HIDL Audio Port name is too long: %zu", port.name.size());
result = BAD_VALUE;
}
- halPort->num_audio_profiles = port.profiles.size();
- if (halPort->num_audio_profiles > AUDIO_PORT_MAX_AUDIO_PROFILES) {
- ALOGE("HIDL Audio Port has too many profiles: %u", halPort->num_audio_profiles);
- halPort->num_audio_profiles = AUDIO_PORT_MAX_AUDIO_PROFILES;
- result = BAD_VALUE;
- }
- for (size_t i = 0; i < halPort->num_audio_profiles; ++i) {
- CONVERT_CHECKED(audioProfileToHal(port.profiles[i], &halPort->audio_profiles[i]), result);
- }
+ CONVERT_CHECKED(audioTransportsToHal(port.transports, halPort), result);
halPort->num_gains = port.gains.size();
if (halPort->num_gains > AUDIO_PORT_MAX_GAINS) {
ALOGE("HIDL Audio Port has too many gains: %u", halPort->num_gains);
@@ -824,6 +833,110 @@
return result;
}
+status_t HidlUtils::audioTransportsFromHal(const struct audio_port_v7& halPort, bool isInput,
+ hidl_vec<AudioTransport>* transports) {
+ if (halPort.num_audio_profiles > AUDIO_PORT_MAX_AUDIO_PROFILES ||
+ halPort.num_extra_audio_descriptors > AUDIO_PORT_MAX_EXTRA_AUDIO_DESCRIPTORS) {
+ ALOGE("%s, too many audio profiles(%u) or extra audio descriptors(%u)", __func__,
+ halPort.num_audio_profiles, halPort.num_extra_audio_descriptors);
+ return BAD_VALUE;
+ }
+ status_t result = NO_ERROR;
+ transports->resize(halPort.num_audio_profiles + halPort.num_extra_audio_descriptors);
+ size_t idx = 0;
+ for (size_t i = 0; i < halPort.num_audio_profiles; ++i) {
+ auto& transport = (*transports)[idx++];
+ transport.audioCapability.profile({});
+ CONVERT_CHECKED(audioProfileFromHal(halPort.audio_profiles[i], isInput,
+ &transport.audioCapability.profile()),
+ result);
+ CONVERT_CHECKED(encapsulationTypeFromHal(halPort.audio_profiles[i].encapsulation_type,
+ &transport.encapsulationType),
+ result);
+ }
+ for (size_t i = 0; i < halPort.num_extra_audio_descriptors; ++i) {
+ switch (halPort.extra_audio_descriptors[i].standard) {
+ case AUDIO_STANDARD_EDID: {
+ const struct audio_extra_audio_descriptor* extraAudioDescriptor =
+ &halPort.extra_audio_descriptors[i];
+ if (extraAudioDescriptor->descriptor_length <= EXTRA_AUDIO_DESCRIPTOR_SIZE) {
+ auto& transport = (*transports)[idx++];
+ transport.audioCapability.edid(
+ hidl_vec<uint8_t>(extraAudioDescriptor->descriptor,
+ extraAudioDescriptor->descriptor +
+ extraAudioDescriptor->descriptor_length));
+ CONVERT_CHECKED(
+ encapsulationTypeFromHal(extraAudioDescriptor->encapsulation_type,
+ &transport.encapsulationType),
+ result);
+ } else {
+ ALOGE("%s, invalid descriptor length %u", __func__,
+ extraAudioDescriptor->descriptor_length);
+ result = BAD_VALUE;
+ }
+ } break;
+ case AUDIO_STANDARD_NONE:
+ default:
+ ALOGE("%s, invalid standard %u", __func__,
+ halPort.extra_audio_descriptors[i].standard);
+ result = BAD_VALUE;
+ break;
+ }
+ }
+ return result;
+}
+
+status_t HidlUtils::audioTransportsToHal(const hidl_vec<AudioTransport>& transports,
+ struct audio_port_v7* halPort) {
+ status_t result = NO_ERROR;
+ halPort->num_audio_profiles = 0;
+ halPort->num_extra_audio_descriptors = 0;
+ for (const auto& transport : transports) {
+ switch (transport.audioCapability.getDiscriminator()) {
+ case AudioTransport::AudioCapability::hidl_discriminator::profile:
+ if (halPort->num_audio_profiles > AUDIO_PORT_MAX_AUDIO_PROFILES) {
+ ALOGE("%s, too many audio profiles", __func__);
+ result = BAD_VALUE;
+ break;
+ }
+ CONVERT_CHECKED(
+ audioProfileToHal(transport.audioCapability.profile(),
+ &halPort->audio_profiles[halPort->num_audio_profiles]),
+ result);
+ CONVERT_CHECKED(encapsulationTypeToHal(
+ transport.encapsulationType,
+ &halPort->audio_profiles[halPort->num_audio_profiles++]
+ .encapsulation_type),
+ result);
+ break;
+ case AudioTransport::AudioCapability::hidl_discriminator::edid:
+ if (halPort->num_extra_audio_descriptors > AUDIO_PORT_MAX_EXTRA_AUDIO_DESCRIPTORS) {
+ ALOGE("%s, too many extra audio descriptors", __func__);
+ result = BAD_VALUE;
+ break;
+ }
+ if (transport.audioCapability.edid().size() > EXTRA_AUDIO_DESCRIPTOR_SIZE) {
+ ALOGE("%s, wrong edid size %zu", __func__,
+ transport.audioCapability.edid().size());
+ result = BAD_VALUE;
+ break;
+ }
+ struct audio_extra_audio_descriptor* extraAudioDescriptor =
+ &halPort->extra_audio_descriptors[halPort->num_extra_audio_descriptors++];
+ extraAudioDescriptor->standard = AUDIO_STANDARD_EDID;
+ extraAudioDescriptor->descriptor_length = transport.audioCapability.edid().size();
+ memcpy(extraAudioDescriptor->descriptor, transport.audioCapability.edid().data(),
+ transport.audioCapability.edid().size() * sizeof(uint8_t));
+
+ CONVERT_CHECKED(encapsulationTypeToHal(transport.encapsulationType,
+ &extraAudioDescriptor->encapsulation_type),
+ result);
+ break;
+ }
+ }
+ return result;
+}
+
status_t HidlUtils::audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
AudioProfile* profile) {
status_t result = NO_ERROR;
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index dd4ca4d..98ecc07 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -126,6 +126,10 @@
static hidl_vec<AudioTag> filterOutNonVendorTags(const hidl_vec<AudioTag>& tags);
static std::vector<std::string> filterOutNonVendorTags(const std::vector<std::string>& tags);
static std::vector<std::string> splitAudioTags(const char* halTags);
+ static status_t audioTransportsFromHal(const struct audio_port_v7& halPort, bool isInput,
+ hidl_vec<AudioTransport>* transports);
+ static status_t audioTransportsToHal(const hidl_vec<AudioTransport>& transports,
+ struct audio_port_v7* halTransport);
private:
static status_t audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
@@ -145,6 +149,10 @@
struct audio_port_config_device_ext* device,
struct audio_port_config_mix_ext* mix,
struct audio_port_config_session_ext* session);
+ static status_t encapsulationTypeFromHal(audio_encapsulation_type_t halEncapsulationType,
+ AudioEncapsulationType* encapsulationType);
+ static status_t encapsulationTypeToHal(const AudioEncapsulationType& encapsulationType,
+ audio_encapsulation_type_t* halEncapsulationType);
#endif // MAJOR_VERSION >= 7
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
index e154453..c9e6fac 100644
--- a/audio/common/all-versions/default/tests/hidlutils_tests.cpp
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -47,6 +47,10 @@
// AUDIO_STREAM_DEFAULT is framework-only
static constexpr audio_stream_type_t kInvalidHalStreamType = static_cast<audio_stream_type_t>(-2);
static constexpr audio_usage_t kInvalidHalUsage = static_cast<audio_usage_t>(0xFFFFFFFFU);
+static constexpr audio_encapsulation_type_t kInvalidEncapsulationType =
+ static_cast<audio_encapsulation_type_t>(0xFFFFFFFFU);
+static constexpr audio_standard_t kInvalidAudioStandard =
+ static_cast<audio_standard_t>(0xFFFFFFFFU);
TEST(HidlUtils, ConvertInvalidChannelMask) {
AudioChannelMask invalid;
@@ -950,6 +954,53 @@
EXPECT_TRUE(audio_port_configs_are_equal(&halConfig, &halConfigBack));
}
+TEST(HidlUtils, ConvertInvalidAudioTransports) {
+ hidl_vec<AudioTransport> invalid;
+ struct audio_port_v7 halInvalid = {};
+ halInvalid.num_audio_profiles = 1;
+ halInvalid.audio_profiles[0].format = kInvalidHalFormat;
+ halInvalid.audio_profiles[0].encapsulation_type = kInvalidEncapsulationType;
+ halInvalid.num_extra_audio_descriptors = 1;
+ halInvalid.extra_audio_descriptors[0].standard = kInvalidAudioStandard;
+ halInvalid.extra_audio_descriptors[0].descriptor_length = EXTRA_AUDIO_DESCRIPTOR_SIZE + 1;
+ EXPECT_EQ(BAD_VALUE,
+ HidlUtils::audioTransportsFromHal(halInvalid, false /*isInput*/, &invalid));
+ invalid.resize(2);
+ AudioProfile invalidProfile;
+ invalidProfile.format = "random string";
+ invalid[0].audioCapability.profile(invalidProfile);
+ invalid[0].encapsulationType = "random string";
+ invalid[0].audioCapability.edid(hidl_vec<uint8_t>(EXTRA_AUDIO_DESCRIPTOR_SIZE + 1));
+ invalid[1].encapsulationType = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioTransportsToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioTransports) {
+ hidl_vec<AudioTransport> transports;
+ transports.resize(2);
+ AudioProfile profile;
+ profile.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+ profile.sampleRates.resize(2);
+ profile.sampleRates[0] = 44100;
+ profile.sampleRates[1] = 48000;
+ profile.channelMasks.resize(2);
+ profile.channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+ profile.channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ transports[0].audioCapability.profile(profile);
+ hidl_vec<uint8_t> shortAudioDescriptor({0x11, 0x06, 0x01});
+ transports[0].encapsulationType =
+ toString(xsd::AudioEncapsulationType::AUDIO_ENCAPSULATION_TYPE_NONE);
+ transports[1].audioCapability.edid(std::move(shortAudioDescriptor));
+ transports[1].encapsulationType =
+ toString(xsd::AudioEncapsulationType::AUDIO_ENCAPSULATION_TYPE_IEC61937);
+ struct audio_port_v7 halPort;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioTransportsToHal(transports, &halPort));
+ hidl_vec<AudioTransport> transportsBack;
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::audioTransportsFromHal(halPort, false /*isInput*/, &transportsBack));
+ EXPECT_EQ(transports, transportsBack);
+}
+
TEST(HidlUtils, ConvertInvalidAudioPort) {
AudioPort invalid;
struct audio_port_v7 halInvalid = {};
@@ -958,8 +1009,10 @@
halInvalid.num_audio_profiles = 1;
halInvalid.audio_profiles[0].format = kInvalidHalFormat;
EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortFromHal(halInvalid, &invalid));
- invalid.profiles.resize(1);
- invalid.profiles[0].format = "random string";
+ invalid.transports.resize(1);
+ AudioProfile invalidProfile;
+ invalidProfile.format = "random string";
+ invalid.transports[0].audioCapability.profile(invalidProfile);
EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortToHal(invalid, &halInvalid));
}
@@ -967,14 +1020,22 @@
AudioPort port = {};
port.id = 42;
port.name = "test";
- port.profiles.resize(1);
- port.profiles[0].format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
- port.profiles[0].sampleRates.resize(2);
- port.profiles[0].sampleRates[0] = 44100;
- port.profiles[0].sampleRates[1] = 48000;
- port.profiles[0].channelMasks.resize(2);
- port.profiles[0].channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
- port.profiles[0].channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ port.transports.resize(2);
+ AudioProfile profile;
+ profile.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+ profile.sampleRates.resize(2);
+ profile.sampleRates[0] = 44100;
+ profile.sampleRates[1] = 48000;
+ profile.channelMasks.resize(2);
+ profile.channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+ profile.channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ port.transports[0].audioCapability.profile(profile);
+ port.transports[0].encapsulationType =
+ toString(xsd::AudioEncapsulationType::AUDIO_ENCAPSULATION_TYPE_NONE);
+ hidl_vec<uint8_t> shortAudioDescriptor({0x11, 0x06, 0x01});
+ port.transports[1].audioCapability.edid(std::move(shortAudioDescriptor));
+ port.transports[1].encapsulationType =
+ toString(xsd::AudioEncapsulationType::AUDIO_ENCAPSULATION_TYPE_IEC61937);
port.gains.resize(1);
port.gains[0].channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
port.ext.device({});
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 70a1a4d..130dfba 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -360,18 +360,43 @@
return Result::NOT_SUPPORTED;
}
-Return<void> Device::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
- audio_port halPort;
- HidlUtils::audioPortToHal(port, &halPort);
- Result retval = analyzeStatus("get_audio_port", mDevice->get_audio_port(mDevice, &halPort));
+template <typename HalPort>
+Return<void> Device::getAudioPortImpl(const AudioPort& port, getAudioPort_cb _hidl_cb,
+ int (*halGetter)(audio_hw_device_t*, HalPort*),
+ const char* halGetterName) {
+ HalPort halPort;
+ if (status_t status = HidlUtils::audioPortToHal(port, &halPort); status != NO_ERROR) {
+ _hidl_cb(analyzeStatus("audioPortToHal", status), port);
+ return Void();
+ }
+ Result retval = analyzeStatus(halGetterName, halGetter(mDevice, &halPort));
AudioPort resultPort = port;
if (retval == Result::OK) {
- HidlUtils::audioPortFromHal(halPort, &resultPort);
+ if (status_t status = HidlUtils::audioPortFromHal(halPort, &resultPort);
+ status != NO_ERROR) {
+ _hidl_cb(analyzeStatus("audioPortFromHal", status), port);
+ return Void();
+ }
}
_hidl_cb(retval, resultPort);
return Void();
}
+#if MAJOR_VERSION <= 6
+Return<void> Device::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
+ return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port, "get_audio_port");
+}
+#else
+Return<void> Device::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
+ if (version() >= AUDIO_DEVICE_API_VERSION_3_2) {
+ // get_audio_port_v7 is mandatory if legacy HAL support this API version.
+ return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port_v7, "get_audio_port_v7");
+ } else {
+ return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port, "get_audio_port");
+ }
+}
+#endif
+
Return<Result> Device::setAudioPortConfig(const AudioPortConfig& config) {
if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
struct audio_port_config halPortConfig;
diff --git a/audio/core/all-versions/default/include/core/default/Device.h b/audio/core/all-versions/default/include/core/default/Device.h
index 5851fc9..94cad53 100644
--- a/audio/core/all-versions/default/include/core/default/Device.h
+++ b/audio/core/all-versions/default/include/core/default/Device.h
@@ -153,6 +153,10 @@
std::tuple<Result, AudioPatchHandle> createOrUpdateAudioPatch(
AudioPatchHandle patch, const hidl_vec<AudioPortConfig>& sources,
const hidl_vec<AudioPortConfig>& sinks);
+ template <typename HalPort>
+ Return<void> getAudioPortImpl(const AudioPort& port, getAudioPort_cb _hidl_cb,
+ int (*halGetter)(audio_hw_device_t*, HalPort*),
+ const char* halGetterName);
// Methods from ParametersUtil.
char* halGetParameters(const char* keys) override;
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
index 657b42d..0b3098b 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -580,12 +580,19 @@
// Starting / resuming of streams is asynchronous at HAL level.
// Sometimes HAL doesn't have enough information until the audio data actually gets
// consumed by the hardware.
- do {
+ bool timedOut = false;
+ res = Result::INVALID_STATE;
+ for (android::base::Timer elapsed;
+ res != Result::OK && !writer.hasError() &&
+ !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+ usleep(kWriteDurationUs);
ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts)));
ASSERT_RESULT(okOrInvalidState, res);
- } while (res != Result::OK);
+ }
+ ASSERT_FALSE(writer.hasError());
+ ASSERT_FALSE(timedOut);
+
uint64_t frames = framesInitial;
- bool timedOut = false;
for (android::base::Timer elapsed;
frames <= framesInitial && !writer.hasError() &&
!(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
@@ -666,11 +673,18 @@
allParams.begin(), allParams.end(), std::back_inserter(pcmParams), [](auto cfg) {
const auto& flags = std::get<PARAM_FLAGS>(cfg);
return xsd::isLinearPcm(std::get<PARAM_CONFIG>(cfg).base.format)
- // MMAP NOIRQ profiles use different reading protocol.
+ // MMAP NOIRQ profiles use different reading protocol,
+ // reading h/w hotword might require Soundtrigger to be active.
&&
- std::find(flags.begin(), flags.end(),
- toString(xsd::AudioInOutFlag::AUDIO_INPUT_FLAG_MMAP_NOIRQ)) ==
- flags.end() &&
+ std::find_if(
+ flags.begin(), flags.end(),
+ [](const auto& flag) {
+ return flag == toString(
+ xsd::AudioInOutFlag::
+ AUDIO_INPUT_FLAG_MMAP_NOIRQ) ||
+ flag == toString(xsd::AudioInOutFlag::
+ AUDIO_INPUT_FLAG_HW_HOTWORD);
+ }) == flags.end() &&
!getCachedPolicyConfig()
.getAttachedSourceDeviceForMixPort(
std::get<PARAM_DEVICE_NAME>(
@@ -690,6 +704,15 @@
InputStreamTest::TearDown();
}
+ bool canQueryCapturePosition() const {
+ auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+ getDeviceName(), getMixPortName());
+ // Returning 'true' when no source is found so the test can fail later with a more clear
+ // problem description.
+ return !maybeSourceAddress.has_value() ||
+ !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType);
+ }
+
void createPatchIfNeeded() {
auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
getDeviceName(), getMixPortName());
@@ -714,6 +737,7 @@
EXPECT_OK(stream->setDevices({maybeSourceAddress.value()}));
}
}
+
void releasePatchIfNeeded() {
if (areAudioPatchesSupported()) {
if (mHasPatch) {
@@ -724,7 +748,42 @@
EXPECT_OK(stream->setDevices({address}));
}
}
- const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+
+ void waitForCapturePositionAdvance(StreamReader& reader, uint64_t* firstPosition = nullptr,
+ uint64_t* lastPosition = nullptr) {
+ static constexpr int kReadDurationUs = 50 * 1000;
+ static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
+ uint64_t framesInitial, ts;
+ // Starting / resuming of streams is asynchronous at HAL level.
+ // Sometimes HAL doesn't have enough information until the audio data actually has been
+ // produced by the hardware. Legacy HALs might return NOT_SUPPORTED when they actually
+ // mean INVALID_STATE.
+ bool timedOut = false;
+ res = Result::INVALID_STATE;
+ for (android::base::Timer elapsed;
+ res != Result::OK && !reader.hasError() &&
+ !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+ usleep(kReadDurationUs);
+ ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
+ ASSERT_RESULT(okOrInvalidStateOrNotSupported, res);
+ }
+ ASSERT_FALSE(reader.hasError());
+ ASSERT_FALSE(timedOut);
+
+ uint64_t frames = framesInitial;
+ for (android::base::Timer elapsed;
+ frames <= framesInitial && !reader.hasError() &&
+ !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+ usleep(kReadDurationUs);
+ ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
+ ASSERT_RESULT(Result::OK, res);
+ }
+ EXPECT_FALSE(timedOut);
+ EXPECT_FALSE(reader.hasError());
+ EXPECT_GT(frames, framesInitial);
+ if (firstPosition) *firstPosition = framesInitial;
+ if (lastPosition) *lastPosition = frames;
+ }
private:
AudioPatchHandle mPatchHandle = {};
@@ -740,47 +799,36 @@
TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionAdvancesWithReads) {
doc::test("Check that the capture position advances with reads");
+ if (!canQueryCapturePosition()) {
+ GTEST_SKIP() << "Capture position retrieval is not possible";
+ }
ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
StreamReader reader(stream.get(), stream->getBufferSize());
ASSERT_TRUE(reader.start());
EXPECT_TRUE(reader.waitForAtLeastOneCycle());
-
- uint64_t framesInitial, ts;
- ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
- ASSERT_RESULT(Result::OK, res);
-
- EXPECT_TRUE(reader.waitForAtLeastOneCycle());
-
- uint64_t frames;
- ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
- ASSERT_RESULT(Result::OK, res);
- EXPECT_GT(frames, framesInitial);
-
- reader.stop();
- releasePatchIfNeeded();
+ ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader));
}
TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionPreservedOnStandby) {
doc::test("Check that the capture position does not reset on standby");
+ if (!canQueryCapturePosition()) {
+ GTEST_SKIP() << "Capture position retrieval is not possible";
+ }
ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
StreamReader reader(stream.get(), stream->getBufferSize());
ASSERT_TRUE(reader.start());
EXPECT_TRUE(reader.waitForAtLeastOneCycle());
- uint64_t framesInitial, ts;
- ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
- ASSERT_RESULT(Result::OK, res);
-
+ uint64_t framesInitial;
+ ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, nullptr, &framesInitial));
reader.pause();
ASSERT_OK(stream->standby());
reader.resume();
- EXPECT_FALSE(reader.hasError());
uint64_t frames;
- ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
- ASSERT_RESULT(Result::OK, res);
+ ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, &frames, nullptr));
EXPECT_GT(frames, framesInitial);
reader.stop();
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index ae1467d..aa7fd8e 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -1194,7 +1194,17 @@
#if MAJOR_VERSION <= 6
address.device = AudioDevice::IN_DEFAULT;
#elif MAJOR_VERSION >= 7
- address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
+ auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+ getDeviceName(), getMixPortName());
+ if (maybeSourceAddress.has_value() &&
+ !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType)) {
+ address = maybeSourceAddress.value();
+ auto& metadata = initMetadata.tracks[0];
+ metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_UNPROCESSED);
+ metadata.channelMask = getConfig().base.channelMask;
+ } else {
+ address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
+ }
#endif
const AudioConfig& config = getConfig();
auto flags = getInputFlags();
@@ -1212,7 +1222,8 @@
#elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
const SinkMetadata initMetadata = {{ {.source = AudioSource::DEFAULT, .gain = 1 } }};
#elif MAJOR_VERSION >= 7
- const SinkMetadata initMetadata = {
+ const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+ SinkMetadata initMetadata = {
{{.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
.gain = 1,
.tags = {},
diff --git a/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp b/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
index 75116af..925fd33 100644
--- a/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
+++ b/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
@@ -33,12 +33,6 @@
// Use nullptr to test error reporting from the worker thread.
explicit TestWorker(TestStream* stream) : mStream(stream) {}
- void ensureWorkerCycled() {
- const size_t cyclesBefore = mWorkerCycles;
- while (mWorkerCycles == cyclesBefore && !hasError()) {
- sched_yield();
- }
- }
size_t getWorkerCycles() const { return mWorkerCycles; }
bool hasWorkerCycleCalled() const { return mWorkerCycles != 0; }
bool hasNoWorkerCycleCalled(useconds_t usec) {
@@ -131,21 +125,21 @@
TEST_P(StreamWorkerTest, Start) {
ASSERT_TRUE(worker.start());
- worker.ensureWorkerCycled();
+ worker.waitForAtLeastOneCycle();
EXPECT_FALSE(worker.hasError());
}
TEST_P(StreamWorkerTest, WorkerError) {
ASSERT_TRUE(worker.start());
stream.error = true;
- worker.ensureWorkerCycled();
+ worker.waitForAtLeastOneCycle();
EXPECT_TRUE(worker.hasError());
EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
}
TEST_P(StreamWorkerTest, PauseResume) {
ASSERT_TRUE(worker.start());
- worker.ensureWorkerCycled();
+ worker.waitForAtLeastOneCycle();
EXPECT_FALSE(worker.hasError());
worker.pause();
EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
@@ -159,7 +153,7 @@
TEST_P(StreamWorkerTest, StopPaused) {
ASSERT_TRUE(worker.start());
- worker.ensureWorkerCycled();
+ worker.waitForAtLeastOneCycle();
EXPECT_FALSE(worker.hasError());
worker.pause();
worker.stop();
@@ -169,7 +163,7 @@
TEST_P(StreamWorkerTest, PauseAfterErrorIgnored) {
ASSERT_TRUE(worker.start());
stream.error = true;
- worker.ensureWorkerCycled();
+ worker.waitForAtLeastOneCycle();
EXPECT_TRUE(worker.hasError());
worker.pause();
EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
@@ -179,7 +173,7 @@
TEST_P(StreamWorkerTest, ResumeAfterErrorIgnored) {
ASSERT_TRUE(worker.start());
stream.error = true;
- worker.ensureWorkerCycled();
+ worker.waitForAtLeastOneCycle();
EXPECT_TRUE(worker.hasError());
worker.resume();
EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
@@ -188,14 +182,14 @@
TEST_P(StreamWorkerTest, WorkerErrorOnResume) {
ASSERT_TRUE(worker.start());
- worker.ensureWorkerCycled();
+ worker.waitForAtLeastOneCycle();
EXPECT_FALSE(worker.hasError());
worker.pause();
EXPECT_FALSE(worker.hasError());
stream.error = true;
EXPECT_FALSE(worker.hasError());
worker.resume();
- worker.ensureWorkerCycled();
+ worker.waitForAtLeastOneCycle();
EXPECT_TRUE(worker.hasError());
EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
}
diff --git a/audio/effect/all-versions/default/util/EffectUtils.cpp b/audio/effect/all-versions/default/util/EffectUtils.cpp
index b4382dc..1156d21 100644
--- a/audio/effect/all-versions/default/util/EffectUtils.cpp
+++ b/audio/effect/all-versions/default/util/EffectUtils.cpp
@@ -25,8 +25,6 @@
#include "util/EffectUtils.h"
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
-
using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
using ::android::hardware::audio::common::utils::EnumBitfield;
@@ -154,6 +152,29 @@
return result;
}
+template <std::size_t N>
+inline hidl_string charBufferFromHal(const char (&halBuf)[N]) {
+ // Even if the original field contains a non-terminated string, hidl_string
+ // adds a NUL terminator.
+ return hidl_string(halBuf, strnlen(halBuf, N));
+}
+
+template <std::size_t N>
+inline status_t charBufferToHal(const hidl_string& str, char (&halBuf)[N], const char* fieldName) {
+ static_assert(N > 0);
+ const size_t halBufChars = N - 1; // Reserve one character for terminating NUL.
+ status_t result = NO_ERROR;
+ size_t strSize = str.size();
+ if (strSize > halBufChars) {
+ ALOGE("%s is too long: %zu (%zu max)", fieldName, strSize, halBufChars);
+ strSize = halBufChars;
+ result = BAD_VALUE;
+ }
+ strncpy(halBuf, str.c_str(), strSize);
+ halBuf[strSize] = '\0';
+ return result;
+}
+
status_t EffectUtils::effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
EffectDescriptor* descriptor) {
UuidUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
@@ -166,9 +187,8 @@
memcpy(descriptor->implementor.data(), halDescriptor.implementor,
descriptor->implementor.size());
#else
- descriptor->name = hidl_string(halDescriptor.name, ARRAY_SIZE(halDescriptor.name));
- descriptor->implementor =
- hidl_string(halDescriptor.implementor, ARRAY_SIZE(halDescriptor.implementor));
+ descriptor->name = charBufferFromHal(halDescriptor.name);
+ descriptor->implementor = charBufferFromHal(halDescriptor.implementor);
#endif
return NO_ERROR;
}
@@ -186,25 +206,11 @@
memcpy(halDescriptor->implementor, descriptor.implementor.data(),
descriptor.implementor.size());
#else
- // According to 'dumpEffectDescriptor' 'name' and 'implementor' must be NUL-terminated.
- size_t nameSize = descriptor.name.size();
- if (nameSize >= ARRAY_SIZE(halDescriptor->name)) {
- ALOGE("effect name is too long: %zu (%zu max)", nameSize,
- ARRAY_SIZE(halDescriptor->name) - 1);
- nameSize = ARRAY_SIZE(halDescriptor->name) - 1;
- result = BAD_VALUE;
- }
- strncpy(halDescriptor->name, descriptor.name.c_str(), nameSize);
- halDescriptor->name[nameSize] = '\0';
- size_t implementorSize = descriptor.implementor.size();
- if (implementorSize >= ARRAY_SIZE(halDescriptor->implementor)) {
- ALOGE("effect implementor is too long: %zu (%zu max)", implementorSize,
- ARRAY_SIZE(halDescriptor->implementor) - 1);
- implementorSize = ARRAY_SIZE(halDescriptor->implementor) - 1;
- result = BAD_VALUE;
- }
- strncpy(halDescriptor->implementor, descriptor.implementor.c_str(), implementorSize);
- halDescriptor->implementor[implementorSize] = '\0';
+ // According to 'dumpEffectDescriptor', 'name' and 'implementor' must be NUL-terminated.
+ CONVERT_CHECKED(charBufferToHal(descriptor.name, halDescriptor->name, "effect name"), result);
+ CONVERT_CHECKED(charBufferToHal(descriptor.implementor, halDescriptor->implementor,
+ "effect implementor"),
+ result);
#endif
return result;
}
diff --git a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
index f3651de..d021fa0 100644
--- a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
+++ b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
@@ -154,3 +154,20 @@
EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &descBack));
EXPECT_EQ(desc, descBack);
}
+
+TEST(EffectUtils, ConvertNameAndImplementor) {
+ for (size_t i = 0; i < EFFECT_STRING_LEN_MAX; ++i) {
+ effect_descriptor_t halDesc{};
+ for (size_t c = 0; c < i; ++c) { // '<' to accommodate NUL terminator.
+ halDesc.name[c] = halDesc.implementor[c] = 'A' + static_cast<char>(c);
+ }
+ EffectDescriptor desc;
+ EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &desc));
+ effect_descriptor_t halDescBack;
+ EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorToHal(desc, &halDescBack));
+ EXPECT_EQ(i, strlen(halDescBack.name));
+ EXPECT_EQ(i, strlen(halDescBack.implementor));
+ EXPECT_EQ(0, strcmp(halDesc.name, halDescBack.name));
+ EXPECT_EQ(0, strcmp(halDesc.implementor, halDescBack.implementor));
+ }
+}
diff --git a/automotive/audiocontrol/aidl/default/Android.bp b/automotive/audiocontrol/aidl/default/Android.bp
index 878bee1..7694bdf 100644
--- a/automotive/audiocontrol/aidl/default/Android.bp
+++ b/automotive/audiocontrol/aidl/default/Android.bp
@@ -27,10 +27,8 @@
init_rc: ["audiocontrol-default.rc"],
vintf_fragments: ["audiocontrol-default.xml"],
vendor: true,
- generated_headers: ["audio_policy_configuration_V7_0"],
- generated_sources: ["audio_policy_configuration_V7_0"],
- header_libs: ["libxsdc-utils"],
shared_libs: [
+ "android.hardware.audio.common@7.0-enums",
"android.frameworks.automotive.powerpolicy-V1-ndk_platform",
"android.hardware.automotive.audiocontrol-V1-ndk_platform",
"libbase",
@@ -38,7 +36,6 @@
"libcutils",
"liblog",
"libpowerpolicyclient",
- "libxml2",
],
srcs: [
"AudioControl.cpp",
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.cpp b/automotive/audiocontrol/aidl/default/AudioControl.cpp
index b076d01..c0bc796 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.cpp
+++ b/automotive/audiocontrol/aidl/default/AudioControl.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#define LOG_TAG "AudioControl"
+// #define LOG_NDEBUG 0
+
#include "AudioControl.h"
#include <aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.h>
@@ -24,7 +27,7 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
-#include <android_audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
#include <private/android_filesystem_config.h>
#include <stdio.h>
@@ -33,6 +36,7 @@
using ::android::base::EqualsIgnoreCase;
using ::android::base::ParseInt;
+using ::std::shared_ptr;
using ::std::string;
namespace xsd {
@@ -68,7 +72,7 @@
const shared_ptr<IFocusListener>& in_listener) {
LOG(DEBUG) << "registering focus listener";
- if (in_listener.get()) {
+ if (in_listener) {
std::atomic_store(&mFocusListener, in_listener);
} else {
LOG(ERROR) << "Unexpected nullptr for listener resulting in no-op.";
@@ -80,9 +84,10 @@
if (isValidValue(value)) {
// Just log in this default mock implementation
LOG(INFO) << "Balance set to " << value;
- } else {
- LOG(ERROR) << "Balance value out of range -1 to 1 at " << value;
+ return ndk::ScopedAStatus::ok();
}
+
+ LOG(ERROR) << "Balance value out of range -1 to 1 at " << value;
return ndk::ScopedAStatus::ok();
}
@@ -90,9 +95,10 @@
if (isValidValue(value)) {
// Just log in this default mock implementation
LOG(INFO) << "Fader set to " << value;
- } else {
- LOG(ERROR) << "Fader value out of range -1 to 1 at " << value;
+ return ndk::ScopedAStatus::ok();
}
+
+ LOG(ERROR) << "Fader value out of range -1 to 1 at " << value;
return ndk::ScopedAStatus::ok();
}
@@ -194,7 +200,7 @@
}
string usage = string(args[1]);
- if (xsd::stringToAudioUsage(usage) == xsd::AudioUsage::UNKNOWN) {
+ if (xsd::isUnknownAudioUsage(usage)) {
dprintf(fd,
"Unknown usage provided: %s. Please see audio_policy_configuration.xsd V7_0 "
"for supported values\n",
@@ -236,7 +242,7 @@
}
string usage = string(args[1]);
- if (xsd::stringToAudioUsage(usage) == xsd::AudioUsage::UNKNOWN) {
+ if (xsd::isUnknownAudioUsage(usage)) {
dprintf(fd,
"Unknown usage provided: %s. Please see audio_policy_configuration.xsd V7_0 "
"for supported values\n",
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.h b/automotive/audiocontrol/aidl/default/AudioControl.h
index ab0b1b3..cca9c44 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.h
+++ b/automotive/audiocontrol/aidl/default/AudioControl.h
@@ -23,8 +23,6 @@
namespace aidl::android::hardware::automotive::audiocontrol {
-using ::std::shared_ptr;
-
class AudioControl : public BnAudioControl {
public:
ndk::ScopedAStatus onAudioFocusChange(const std::string& in_usage, int32_t in_zoneId,
@@ -34,7 +32,7 @@
ndk::ScopedAStatus onDevicesToMuteChange(
const std::vector<MutingInfo>& in_mutingInfos) override;
ndk::ScopedAStatus registerFocusListener(
- const shared_ptr<IFocusListener>& in_listener) override;
+ const std::shared_ptr<IFocusListener>& in_listener) override;
ndk::ScopedAStatus setBalanceTowardRight(float in_value) override;
ndk::ScopedAStatus setFadeTowardFront(float in_value) override;
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
@@ -44,7 +42,7 @@
// a single instance of CarAudioService. As such, it doesn't have explicit serialization.
// If a different AudioControl implementation were to have multiple threads leveraging this
// listener, then it should also include mutexes or make the listener atomic.
- shared_ptr<IFocusListener> mFocusListener;
+ std::shared_ptr<IFocusListener> mFocusListener;
binder_status_t cmdHelp(int fd) const;
binder_status_t cmdRequestFocus(int fd, const char** args, uint32_t numArgs);
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 9d0b9ec..19b0a35 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -1149,6 +1149,22 @@
{
.config =
{
+ .prop = toInt(VehicleProperty::UNIX_TIME),
+ .access = VehiclePropertyAccess::READ_WRITE,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ },
+ },
+ {
+ .config =
+ {
+ .prop = toInt(VehicleProperty::STORAGE_ENCRYPTION_BINDING_SEED),
+ .access = VehiclePropertyAccess::READ_WRITE,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ },
+ },
+ {
+ .config =
+ {
.prop = toInt(VehicleProperty::WATCHDOG_ALIVE),
.access = VehiclePropertyAccess::WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
@@ -1206,7 +1222,7 @@
{
.config =
{
- .prop = toInt(VehicleProperty::CLUSTER_NAVIGATION_STATE_LEGACY),
+ .prop = toInt(VehicleProperty::CLUSTER_NAVIGATION_STATE),
.access = VehiclePropertyAccess::WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
},
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 9ecb2d5..1fe4bac 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -1348,6 +1348,48 @@
| VehicleArea:GLOBAL),
/**
+ * Current date and time, encoded as Unix time (in milliseconds).
+ * This value denotes the number of milliseconds seconds that have
+ * elapsed since 1/1/1970 UTC.
+ *
+ * Reading this value will give you the system’s time. This can be
+ * useful to synchronize other vehicle systems (dash clock etc).
+ *
+ * Writing this value will update the ‘ExternalTimeSuggestion’
+ * value (if enabled). This value may be consumed by the “Time
+ * Detector Service”, if other sources do not have a higher
+ * priority. For information on how to adjust time source
+ * priorities see Time Detector Service documentation.
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ * @unit VehicleUnit:MILLI_SECS
+ */
+ UNIX_TIME = (
+ 0x0606
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:INT64
+ | VehicleArea:GLOBAL),
+
+ /**
+ * External encryption binding seed.
+ *
+ * This value is mixed with the local key storage encryption key.
+ * This property holds 16 bytes, and is expected to be persisted on an ECU separate from
+ * the IVI. The property is initially set by AAOS, who generates it using a CSRNG.
+ * AAOS will then read the property on subsequent boots. The binding seed is expected to be
+ * reliably persisted. Any loss of the seed results in a factory reset of the IVI.
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ STORAGE_ENCRYPTION_BINDING_SEED = (
+ 0x0607
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:BYTES
+ | VehicleArea:GLOBAL),
+
+ /**
* Outside temperature
*
* @change_mode VehiclePropertyChangeMode:CONTINUOUS
@@ -3159,7 +3201,7 @@
* @change_mode VehiclePropertyChangeMode:ON_CHANGE
* @access VehiclePropertyAccess:WRITE
*/
- CLUSTER_NAVIGATION_STATE_LEGACY = (
+ CLUSTER_NAVIGATION_STATE = (
0x0F38
| VehiclePropertyGroup:SYSTEM
| VehiclePropertyType:BYTES
@@ -3623,6 +3665,7 @@
US_GALLON = 0x42,
IMPERIAL_GALLON = 0x43,
NANO_SECS = 0x50,
+ MILLI_SECS = 0x51,
SECS = 0x53,
YEAR = 0x59,
diff --git a/automotive/vehicle/2.0/vts/functional/Android.bp b/automotive/vehicle/2.0/vts/functional/Android.bp
index 9f1dd6f..e64e942 100644
--- a/automotive/vehicle/2.0/vts/functional/Android.bp
+++ b/automotive/vehicle/2.0/vts/functional/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
cc_test {
name: "VtsHalAutomotiveVehicleV2_0TargetTest",
defaults: [
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
index 205429b..9033989 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
@@ -34,17 +34,17 @@
package android.hardware.biometrics.face;
@VintfStability
interface ISession {
- void generateChallenge(in int cookie);
- void revokeChallenge(in int cookie, in long challenge);
- android.hardware.biometrics.common.ICancellationSignal enroll(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat, in android.hardware.biometrics.face.EnrollmentType type, in android.hardware.biometrics.face.Feature[] features, in android.hardware.common.NativeHandle previewSurface);
- android.hardware.biometrics.common.ICancellationSignal authenticate(in int cookie, in long operationId);
- android.hardware.biometrics.common.ICancellationSignal detectInteraction(in int cookie);
- void enumerateEnrollments(in int cookie);
- void removeEnrollments(in int cookie, in int[] enrollmentIds);
- void getFeatures(in int cookie, in int enrollmentId);
- void setFeature(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat, in int enrollmentId, in android.hardware.biometrics.face.Feature feature, boolean enabled);
- void getAuthenticatorId(in int cookie);
- void invalidateAuthenticatorId(in int cookie);
- void resetLockout(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat);
- void close(in int cookie);
+ void generateChallenge();
+ void revokeChallenge(in long challenge);
+ android.hardware.biometrics.common.ICancellationSignal enroll(in android.hardware.keymaster.HardwareAuthToken hat, in android.hardware.biometrics.face.EnrollmentType type, in android.hardware.biometrics.face.Feature[] features, in android.hardware.common.NativeHandle previewSurface);
+ android.hardware.biometrics.common.ICancellationSignal authenticate(in long operationId);
+ android.hardware.biometrics.common.ICancellationSignal detectInteraction();
+ void enumerateEnrollments();
+ void removeEnrollments(in int[] enrollmentIds);
+ void getFeatures(in int enrollmentId);
+ void setFeature(in android.hardware.keymaster.HardwareAuthToken hat, in int enrollmentId, in android.hardware.biometrics.face.Feature feature, boolean enabled);
+ void getAuthenticatorId();
+ void invalidateAuthenticatorId();
+ void resetLockout(in android.hardware.keymaster.HardwareAuthToken hat);
+ void close();
}
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISessionCallback.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISessionCallback.aidl
index b0bfa30..2bb053a 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISessionCallback.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISessionCallback.aidl
@@ -34,7 +34,6 @@
package android.hardware.biometrics.face;
@VintfStability
interface ISessionCallback {
- void onStateChanged(in int cookie, in android.hardware.biometrics.face.SessionState state);
void onChallengeGenerated(in long challenge);
void onChallengeRevoked(in long challenge);
void onAuthenticationFrame(in android.hardware.biometrics.face.AuthenticationFrame frame);
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
index 66c7c38..9734873 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
@@ -23,11 +23,25 @@
import android.hardware.keymaster.HardwareAuthToken;
/**
- * A session is a collection of immutable state (sensorId, userId), mutable state (SessionState),
- * methods available for the framework to call, and a callback (ISessionCallback) to notify the
- * framework about the events and results. A session is used to establish communication between
- * the framework and the HAL.
+ * Operations that can be performed for unique sessions retrieved via IFace#createSession.
+ * Operations defined within this interface can be divided into the following categories:
+ * 1) Cancellable operations. These are usually the operations that can execute for several
+ * minutes. To allow for cancellation, they return an instance of ICancellationSignal that
+ * lets the framework cancel them by calling ICancellationSignal#cancel. If such an operation
+ * is cancelled, it must notify the framework by calling ISessionCallback#onError with
+ * Error::CANCELED.
+ * 2) Non-cancellable operations. Such operations cannot be cancelled once started.
+ *
+ * The lifecycle of an operation ends when one of its terminal callbacks is called. For example,
+ * ISession#authenticate is considered completed when any of the following callbacks is called:
+ * ISessionCallback#onError, ISessionCallback#onAuthenticationSucceeded,
+ * ISessionCallback#onAuthenticationFailed.
+ *
+ * ISession only supports execution of one operation at a time, regardless of whether it's
+ * cancellable or not. The framework must wait for a corresponding callback indicating the end of
+ * the current operation before a new operation can be started.
*/
+
@VintfStability
interface ISession {
/**
@@ -55,7 +69,7 @@
*
* Note that this interface allows multiple in-flight challenges. Invoking generateChallenge
* twice does not invalidate the first challenge. The challenge is invalidated only when:
- * 1) Its lifespan exceeds the HAL's internal challenge timeout
+ * 1) Its lifespan exceeds the challenge timeout defined in the TEE.
* 2) IFingerprint#revokeChallenge is invoked
*
* For example, the following is a possible table of valid challenges:
@@ -68,9 +82,8 @@
* | 0 | 10 | <Time4> | <Random4> |
* ----------------------------------------------
*
- * @param cookie A unique number identifying this operation
*/
- void generateChallenge(in int cookie);
+ void generateChallenge();
/**
* revokeChallenge:
@@ -79,10 +92,9 @@
* parameters is requested, the implementation must still notify the framework using the
* provided callback.
*
- * @param cookie A unique number identifying this operation
* @param challenge Challenge that should be revoked.
*/
- void revokeChallenge(in int cookie, in long challenge);
+ void revokeChallenge(in long challenge);
/**
* getEnrollmentConfig:
@@ -101,19 +113,13 @@
*
* A request to add a face enrollment.
*
- * Once the HAL is able to start processing the enrollment request, it must notify the framework
- * via ISessionCallback#onStateChanged with SessionState::ENROLLING.
- *
* At any point during enrollment, if a non-recoverable error occurs, the HAL must notify the
- * framework via ISessionCallback#onError with the applicable enrollment-specific error, and
- * then send ISessionCallback#onStateChanged(cookie, SessionState::IDLING) if no subsequent
- * operation is in the queue.
+ * framework via ISessionCallback#onError with the applicable enrollment-specific error.
*
* Before capturing face data, the implementation must first verify the authenticity and
* integrity of the provided HardwareAuthToken. In addition, it must check that the challenge
* within the provided HardwareAuthToken is valid. See ISession#generateChallenge. If any of
- * the above checks fail, the framework must be notified via ISessionCallback#onError and the
- * HAL must notify the framework when it returns to the idle state. See
+ * the above checks fail, the framework must be notified using ISessionCallback#onError with
* Error::UNABLE_TO_PROCESS.
*
* During enrollment, the implementation may notify the framework via
@@ -121,15 +127,12 @@
* can be invoked multiple times if necessary. Similarly, the framework may be notified of
* enrollment progress changes via ISessionCallback#onEnrollmentProgress. Once the framework is
* notified that there are 0 "remaining" steps, the framework may cache the "enrollmentId". See
- * ISessionCallback#onEnrollmentProgress for more info. The HAL must notify the framework once
- * it returns to the idle state.
+ * ISessionCallback#onEnrollmentProgress for more info.
*
- * When a finger is successfully added and before the framework is notified of remaining=0, the
- * implementation MUST update and associate this (sensorId, userId) pair with a new new
+ * When a face is successfully added and before the framework is notified of remaining=0, the
+ * implementation MUST update and associate this (sensorId, userId) pair with a new
* entropy-encoded random identifier. See ISession#getAuthenticatorId for more information.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
* @param hat See above documentation.
* @param enrollmentType See the EnrollmentType enum.
* @param features See the Feature enum.
@@ -139,7 +142,7 @@
* @return ICancellationSignal An object that can be used by the framework to cancel this
* operation.
*/
- ICancellationSignal enroll(in int cookie, in HardwareAuthToken hat, in EnrollmentType type,
+ ICancellationSignal enroll(in HardwareAuthToken hat, in EnrollmentType type,
in Feature[] features, in NativeHandle previewSurface);
/**
@@ -147,13 +150,8 @@
*
* A request to start looking for faces to authenticate.
*
- * Once the HAL is able to start processing the authentication request, it must notify framework
- * via ISessionCallback#onStateChanged with SessionState::AUTHENTICATING.
- *
* At any point during authentication, if a non-recoverable error occurs, the HAL must notify
- * the framework via ISessionCallback#onError with the applicable authentication-specific error,
- * and then send ISessionCallback#onStateChanged(cookie, SessionState::IDLING) if no
- * subsequent operation is in the queue.
+ * the framework via ISessionCallback#onError with the applicable authentication-specific error.
*
* During authentication, the implementation may notify the framework via
* ISessionCallback#onAcquired with messages that may be used to guide the user. This callback
@@ -175,8 +173,6 @@
* must be set with the operationId passed in during #authenticate. If the sensor is NOT
* SensorStrength::STRONG, the HardwareAuthToken MUST be null.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
* @param operationId For sensors configured as SensorStrength::STRONG, this must be used ONLY
* upon successful authentication and wrapped in the HardwareAuthToken's
* "challenge" field and sent to the framework via
@@ -190,7 +186,7 @@
* @return ICancellationSignal An object that can be used by the framework to cancel this
* operation.
*/
- ICancellationSignal authenticate(in int cookie, in long operationId);
+ ICancellationSignal authenticate(in long operationId);
/**
* detectInteraction:
@@ -199,17 +195,12 @@
* SensorProps#supportsDetectInteraction is true. If invoked on implementations that do not
* support this functionality, the HAL must respond with ISession#onError(UNABLE_TO_PROCESS, 0).
*
- * Once the HAL is able to start processing this request, it must notify the framework via
- * ISessionCallback#onStateChanged with SessionState::DETECTING_INTERACTION.
- *
* The framework will use this method in cases where determing user presence is required, but
* identifying/authentication is not. For example, when the device is encrypted (first boot) or
* in lockdown mode.
*
* At any point during detectInteraction, if a non-recoverable error occurs, the HAL must notify
- * the framework via ISessionCallback#onError with the applicable error, and then send
- * ISessionCallback#onStateChanged(cookie, SessionState::IDLING) if no subsequent operation is
- * in the queue.
+ * the framework via ISessionCallback#onError with the applicable error.
*
* The implementation must only check for a face-like image was detected (e.g. to
* minimize interactions due to non-face objects), and the lockout counter must not
@@ -222,17 +213,15 @@
* 1) Any face is detected and the framework is notified via
* ISessionCallback#onInteractiondetected
* 2) The operation was cancelled by the framework (see ICancellationSignal)
- * 3) The HAL ends the operation, for example when a subsequent operation pre-empts this one.
+ * 3) An error occurred, for example ERROR::TIMEOUT
*
* Note that if the operation is canceled, the implementation must notify the framework via
* ISessionCallback#onError with Error::CANCELED.
*
- * @param cookie An identifier used to track subsystem operations related to this call path.
- * The framework will guarantee that it is unique per ISession.
* @return ICancellationSignal An object that can be used by the framework to cancel this
* operation.
*/
- ICancellationSignal detectInteraction(in int cookie);
+ ICancellationSignal detectInteraction();
/*
* enumerateEnrollments:
@@ -240,32 +229,22 @@
* A request to enumerate (list) the enrollments for this (sensorId, userId) pair. The
* framework typically uses this to ensure that its cache is in sync with the HAL.
*
- * Once the HAL is able to start processing this request, it must notify the framework via
- * ISessionCallback#onStateChanged with SessionState::ENUMERATING_ENROLLMENTS.
- *
* The implementation must then notify the framework with a list of enrollments applicable
* for the current session via ISessionCallback#onEnrollmentsEnumerated.
*
- * @param cookie An identifier used to track subsystem operations related to this call path.
- * The framework will guarantee that it is unique per ISession.
*/
- void enumerateEnrollments(in int cookie);
+ void enumerateEnrollments();
/**
* removeEnrollments:
*
* A request to remove the enrollments for this (sensorId, userId) pair.
*
- * Once the HAL is able to start processing this request, it must notify the framework via
- * ISessionCallback#onStateChanged with SessionState::REMOVING_ENROLLMENTS.
- *
* After removing the enrollmentIds from everywhere necessary (filesystem, secure subsystems,
* etc), the implementation must notify the framework via ISessionCallback#onEnrollmentsRemoved.
*
- * @param cookie An identifier used to track subsystem operations related to this call path.
- * The framework will guarantee that it is unique per ISession.
*/
- void removeEnrollments(in int cookie, in int[] enrollmentIds);
+ void removeEnrollments(in int[] enrollmentIds);
/**
* getFeatures:
@@ -273,20 +252,14 @@
* Returns a list of currently enabled features for the provided enrollmentId.
*
* If the enrollmentId is invalid, the HAL must invoke ISessionCallback#onError with
- * Error::UNABLE_TO_PROCESS and return to SessionState::IDLING if no subsequent work is in the
- * queue.
- *
- * Once the HAL is able to start processing this request, it must notify the framework by using
- * ISessionCallback#onStateChanged with SessionState::GETTING_FEATURES.
+ * Error::UNABLE_TO_PROCESS.
*
* The HAL must notify the framework about the result by calling
* ISessionCallback#onFeaturesRetrieved.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
* @param enrollmentId the ID of the enrollment for which the features are requested.
*/
- void getFeatures(in int cookie, in int enrollmentId);
+ void getFeatures(in int enrollmentId);
/**
* setFeature:
@@ -296,24 +269,18 @@
* (see @param hat). The HAL must verify the hat before changing any feature state.
*
* If either the hat or enrollmentId is invalid, the HAL must invoke ISessionCallback#onError
- * with Error::UNABLE_TO_PROCESS and return to SessionState::IDLING if no subsequent work is in
- * the queue.
- *
- * Once the HAL is able to start processing this request, it must notify the framework by using
- * ISessionCallback#onStateChanged with SessionState::SETTING_FEATURE.
+ * with Error::UNABLE_TO_PROCESS.
*
* After the feature is successfully set, the HAL must notify the framework by calling
* ISessionCallback#onFeatureSet.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
* @param hat HardwareAuthToken See above documentation.
* @param enrollmentId the ID of the enrollment for which the feature update is requested.
* @param feature The feature to be enabled or disabled.
* @param enabled Whether the provided features should be enabled or disabled.
*/
- void setFeature(in int cookie, in HardwareAuthToken hat, in int enrollmentId,
- in Feature feature, boolean enabled);
+ void setFeature(
+ in HardwareAuthToken hat, in int enrollmentId, in Feature feature, boolean enabled);
/**
* getAuthenticatorId:
@@ -341,10 +308,8 @@
* 3) MUST not change if a face is deleted.
* 4) MUST be an entropy-encoded random number
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
*/
- void getAuthenticatorId(in int cookie);
+ void getAuthenticatorId();
/**
* invalidateAuthenticatorId:
@@ -368,10 +333,8 @@
* for more details). As such, the framework would coordinate invalidation across multiple
* biometric HALs as necessary.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
*/
- void invalidateAuthenticatorId(in int cookie);
+ void invalidateAuthenticatorId();
/**
* resetLockout:
@@ -382,8 +345,7 @@
* 2) Verify that the timestamp provided within the HAT is relatively recent (e.g. on the
* order of minutes, not hours).
* If either of the checks fail, the HAL must invoke ISessionCallback#onError with
- * Error::UNABLE_TO_PROCESS and return to SessionState::IDLING if no subsequent work is in the
- * queue.
+ * Error::UNABLE_TO_PROCESS.
*
* Upon successful verification, the HAL must clear the lockout counter and notify the framework
* via ISessionCallback#onLockoutCleared.
@@ -414,27 +376,20 @@
* See the Android CDD section 7.3.10 for the full set of lockout and rate-limiting
* requirements.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
* @param hat HardwareAuthToken See above documentation.
*/
- void resetLockout(in int cookie, in HardwareAuthToken hat);
+ void resetLockout(in HardwareAuthToken hat);
/*
* Close this session and allow the HAL to release the resources associated with this session.
*
- * A session can only be closed when it's in SessionState::IDLING. Closing a session will
- * result in a ISessionCallback#onStateChanged call with SessionState::CLOSED.
- *
- * If a session is unresponsive or stuck in a state other than SessionState::CLOSED,
- * IFace#reset could be used as a last resort to terminate the session and recover the HAL
- * from a bad state.
+ * A session can only be closed when the HAL is idling, i.e. not performing any operations.
+ * If the HAL is busy performing a cancellable operation, the operation must be explicitly
+ * cancelled with a call to ICancellationSignal#cancel before the session can be closed.
*
* All sessions must be explicitly closed. Calling IFace#createSession while there is an active
* session is considered an error.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
*/
- void close(in int cookie);
+ void close();
}
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/ISessionCallback.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/ISessionCallback.aidl
index c1aa3fc..a2601e7 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/ISessionCallback.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/ISessionCallback.aidl
@@ -21,17 +21,11 @@
import android.hardware.biometrics.face.EnrollmentFrame;
import android.hardware.biometrics.face.Error;
import android.hardware.biometrics.face.Feature;
-import android.hardware.biometrics.face.SessionState;
import android.hardware.keymaster.HardwareAuthToken;
@VintfStability
interface ISessionCallback {
/**
- * Used to notify the framework of session state changes. See ISession for more information.
- */
- void onStateChanged(in int cookie, in SessionState state);
-
- /**
* Notifies the framework when a challenge is successfully generated.
*/
void onChallengeGenerated(in long challenge);
@@ -42,9 +36,9 @@
void onChallengeRevoked(in long challenge);
/**
- * This method must only be used to notify the framework during the following states:
- * 1) SessionState::AUTHENTICATING
- * 2) SessionState::DETECTING_INTERACTION
+ * This method must only be used to notify the framework during the following operations:
+ * 1) ISession#authenticate
+ * 2) ISession#detectInteraction
*
* These messages may be used to provide user guidance multiple times if necessary per
* operation.
@@ -54,8 +48,8 @@
void onAuthenticationFrame(in AuthenticationFrame frame);
/**
- * This method must only be used to notify the framework during the SessionState::ENROLLING
- * state.
+ * This method must only be used to notify the framework during the ISession#enroll
+ * operation.
*
* These messages may be used to provide user guidance multiple times if necessary per
* operation.
@@ -65,18 +59,18 @@
void onEnrollmentFrame(in EnrollmentFrame frame);
/**
- * This method must only be used to notify the framework during the following states:
- * 1) SessionState::ENROLLING
- * 2) SessionState::AUTHENTICATING
- * 3) SessionState::DETECTING_INTERACTION
- * 4) SessionState::INVALIDATING_AUTHENTICATOR_ID
- * 5) SessionState::RESETTING_LOCKOUT
+ * This method must only be used to notify the framework during the following operations:
+ * 1) ISession#enroll
+ * 2) ISession#authenticate
+ * 3) ISession#detectInteraction
+ * 4) ISession#invalidateAuthenticatorId
+ * 5) ISession#resetLockout
*
* These messages may be used to notify the framework or user that a non-recoverable error
* has occurred. The operation is finished, and the HAL must proceed with the next operation
- * or return to SessionState::IDLING if the queue is empty.
+ * or return to the idling state.
*
- * Note that cancellation (see common::ICancellationSignal) and preemption most be followed with
+ * Note that cancellation (see common::ICancellationSignal) and preemption must be followed with
* an Error::CANCELED message.
*
* @param error See the Error enum.
@@ -88,8 +82,7 @@
void onError(in Error error, in int vendorCode);
/**
- * This method must only be used to notify the framework during the following state:
- * 1) SessionState::ENROLLING
+ * This method must only be used to notify the framework during the ISession#enroll operation.
*
* @param enrollmentId Unique stable identifier for the enrollment that's being added by this
* ISession#enroll invocation.
@@ -98,7 +91,7 @@
void onEnrollmentProgress(in int enrollmentId, int remaining);
/**
- * This method must only be used to notify the framework during SessionState::AUTHENTICATING.
+ * This method must only be used to notify the framework during ISession#authenticate.
*
* Used to notify the framework about a successful authentication. This ends the authentication
* lifecycle.
@@ -112,7 +105,7 @@
void onAuthenticationSucceeded(in int enrollmentId, in HardwareAuthToken hat);
/**
- * This method must only be used to notify the framework during SessionState::AUTHENTICATING.
+ * This method must only be used to notify the framework during ISession#authenticate.
*
* Used to notify the framework about a failed authentication. This ends the authentication
* lifecycle.
@@ -120,7 +113,7 @@
void onAuthenticationFailed();
/**
- * This method must only be used to notify the framework during SessionState::AUTHENTICATING.
+ * This method must only be used to notify the framework during ISession#authenticate.
*
* Authentication is locked out due to too many unsuccessful attempts. This is a rate-limiting
* lockout, and authentication can be restarted after a period of time. See
@@ -133,7 +126,7 @@
void onLockoutTimed(in long durationMillis);
/**
- * This method must only be used to notify the framework during SessionState::AUTHENTICATING.
+ * This method must only be used to notify the framework during ISession#authenticate.
*
* Authentication is disabled until the user unlocks with their device credential
* (PIN/Pattern/Password). See ISession#resetLockout.
@@ -160,7 +153,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::DETECTING_INTERACTION
+ * ISession#detectInteraction
*
* Notifies the framework that user interaction occurred. See ISession#detectInteraction.
*/
@@ -168,7 +161,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::ENUMERATING_ENROLLMENTS.
+ * ISession#enumerateEnrollments.
*
* Notifies the framework of the current enrollments. See ISession#enumerateEnrollments.
*
@@ -177,7 +170,7 @@
void onEnrollmentsEnumerated(in int[] enrollmentIds);
/**
- * This method must only be used to notify the framework during SessionState::GETTING_FEATURES.
+ * This method must only be used to notify the framework during ISession#getFeatures.
*
* Provides a list of features that are currently enabled for the given enrollmentId.
*
@@ -187,7 +180,7 @@
void onFeaturesRetrieved(in Feature[] features, in int enrollmentId);
/**
- * This method must only be used to notify the framework during SessionState::SETTING_FEATURE.
+ * This method must only be used to notify the framework during ISession#setFeature.
*
* Notifies the framework that ISession#setFeature has completed.
*
@@ -198,7 +191,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::REMOVING_ENROLLMENTS.
+ * ISession#removeEnrollments.
*
* Notifies the framework that the specified enrollments are removed.
*
@@ -208,7 +201,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::GETTING_AUTHENTICATOR_ID.
+ * ISession#getAuthenticatorId.
*
* Notifies the framework with the authenticatorId corresponding to this session's
* (userId, sensorId) pair.
@@ -219,7 +212,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::INVALIDATING_AUTHENTICATOR_ID.
+ * ISession#invalidateAuthenticatorId.
*
* See ISession#invalidateAuthenticatorId for more information.
*
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/SessionState.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/SessionState.aidl
deleted file mode 100644
index afde4eb..0000000
--- a/biometrics/face/aidl/android/hardware/biometrics/face/SessionState.aidl
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.biometrics.face;
-
-@VintfStability
-@Backing(type="byte")
-enum SessionState {
- /**
- * The HAL is not processing any session requests.
- */
- IDLING,
-
- /**
- * The session has been closed by the client.
- */
- CLOSED,
-
- /**
- * The HAL is processing the ISession#generateChallenge request.
- */
- GENERATING_CHALLENGE,
-
- /**
- * The HAL is processing the ISession#revokeChallenge request.
- */
- REVOKING_CHALLENGE,
-
- /**
- * The HAL is processing the ISession#enroll request.
- */
- ENROLLING,
-
- /**
- * The HAL is processing the ISession#authenticate request.
- */
- AUTHENTICATING,
-
- /**
- * The HAL is processing the ISession#detectInteraction request.
- */
- DETECTING_INTERACTION,
-
- /**
- * The HAL is processing the ISession#enumerateEnrollments request.
- */
- ENUMERATING_ENROLLMENTS,
-
- /**
- * The HAL is processing the ISession#removeEnrollments request.
- */
- REMOVING_ENROLLMENTS,
-
- /**
- * The HAL is processing the ISession#getFeatures request.
- */
- GETTING_FEATURES,
-
- /**
- * The HAL is processing the ISession#setFeature request.
- */
- SETTING_FEATURE,
-
- /**
- * The HAL is processing the ISession#getAuthenticatorId request.
- */
- GETTING_AUTHENTICATOR_ID,
-
- /**
- * The HAL is processing the ISession#invalidateAuthenticatorId request.
- */
- INVALIDATING_AUTHENTICATOR_ID,
-
- /**
- * The HAL is processing the ISession#resetLockout request.
- */
- RESETTING_LOCKOUT
-}
diff --git a/biometrics/face/aidl/default/Session.cpp b/biometrics/face/aidl/default/Session.cpp
index ce6c557..b5eb717 100644
--- a/biometrics/face/aidl/default/Session.cpp
+++ b/biometrics/face/aidl/default/Session.cpp
@@ -30,119 +30,105 @@
ndk::ScopedAStatus cancel() override {
cb_->onError(Error::CANCELED, 0 /* vendorCode */);
- cb_->onStateChanged(0, SessionState::IDLING);
return ndk::ScopedAStatus::ok();
}
};
Session::Session(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
-ndk::ScopedAStatus Session::generateChallenge(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::generateChallenge() {
LOG(INFO) << "generateChallenge";
if (cb_) {
- cb_->onStateChanged(0, SessionState::GENERATING_CHALLENGE);
cb_->onChallengeGenerated(0);
- cb_->onStateChanged(0, SessionState::IDLING);
}
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::revokeChallenge(int32_t /*cookie*/, int64_t challenge) {
+ndk::ScopedAStatus Session::revokeChallenge(int64_t challenge) {
LOG(INFO) << "revokeChallenge";
if (cb_) {
- cb_->onStateChanged(0, SessionState::REVOKING_CHALLENGE);
cb_->onChallengeRevoked(challenge);
- cb_->onStateChanged(0, SessionState::IDLING);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::enroll(
- int32_t /*cookie*/, const keymaster::HardwareAuthToken& /*hat*/,
- EnrollmentType /*enrollmentType*/, const std::vector<Feature>& /*features*/,
- const NativeHandle& /*previewSurface*/,
+ const keymaster::HardwareAuthToken& /*hat*/, EnrollmentType /*enrollmentType*/,
+ const std::vector<Feature>& /*features*/, const NativeHandle& /*previewSurface*/,
std::shared_ptr<biometrics::common::ICancellationSignal>* /*return_val*/) {
LOG(INFO) << "enroll";
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::authenticate(int32_t /*cookie*/, int64_t /*keystoreOperationId*/,
+ndk::ScopedAStatus Session::authenticate(int64_t /*keystoreOperationId*/,
std::shared_ptr<common::ICancellationSignal>* return_val) {
LOG(INFO) << "authenticate";
if (cb_) {
- cb_->onStateChanged(0, SessionState::AUTHENTICATING);
+ cb_->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
}
*return_val = SharedRefBase::make<CancellationSignal>(cb_);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::detectInteraction(
- int32_t /*cookie*/, std::shared_ptr<common::ICancellationSignal>* /*return_val*/) {
+ std::shared_ptr<common::ICancellationSignal>* /*return_val*/) {
LOG(INFO) << "detectInteraction";
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::enumerateEnrollments(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::enumerateEnrollments() {
LOG(INFO) << "enumerateEnrollments";
if (cb_) {
- cb_->onStateChanged(0, SessionState::ENUMERATING_ENROLLMENTS);
cb_->onEnrollmentsEnumerated(std::vector<int32_t>());
- cb_->onStateChanged(0, SessionState::IDLING);
}
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::removeEnrollments(int32_t /*cookie*/,
- const std::vector<int32_t>& /*enrollmentIds*/) {
+ndk::ScopedAStatus Session::removeEnrollments(const std::vector<int32_t>& /*enrollmentIds*/) {
LOG(INFO) << "removeEnrollments";
if (cb_) {
- cb_->onStateChanged(0, SessionState::REMOVING_ENROLLMENTS);
cb_->onEnrollmentsRemoved(std::vector<int32_t>());
- cb_->onStateChanged(0, SessionState::IDLING);
}
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::getFeatures(int32_t /*cookie*/, int32_t /*enrollmentId*/) {
+ndk::ScopedAStatus Session::getFeatures(int32_t /*enrollmentId*/) {
LOG(INFO) << "getFeatures";
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::setFeature(int32_t /*cookie*/,
- const keymaster::HardwareAuthToken& /*hat*/,
+ndk::ScopedAStatus Session::setFeature(const keymaster::HardwareAuthToken& /*hat*/,
int32_t /*enrollmentId*/, Feature /*feature*/,
bool /*enabled*/) {
LOG(INFO) << "setFeature";
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::getAuthenticatorId(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::getAuthenticatorId() {
LOG(INFO) << "getAuthenticatorId";
if (cb_) {
- cb_->onStateChanged(0, SessionState::GETTING_AUTHENTICATOR_ID);
cb_->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
- cb_->onStateChanged(0, SessionState::IDLING);
}
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::invalidateAuthenticatorId(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::invalidateAuthenticatorId() {
LOG(INFO) << "invalidateAuthenticatorId";
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::resetLockout(int32_t /*cookie*/,
- const keymaster::HardwareAuthToken& /*hat*/) {
+ndk::ScopedAStatus Session::resetLockout(const keymaster::HardwareAuthToken& /*hat*/) {
LOG(INFO) << "resetLockout";
if (cb_) {
- cb_->onStateChanged(0, SessionState::RESETTING_LOCKOUT);
cb_->onLockoutCleared();
- cb_->onStateChanged(0, SessionState::IDLING);
}
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::close(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::close() {
+ if (cb_) {
+ cb_->onSessionClosed();
+ }
return ndk::ScopedAStatus::ok();
}
diff --git a/biometrics/face/aidl/default/Session.h b/biometrics/face/aidl/default/Session.h
index eb9ae83..73cdf08 100644
--- a/biometrics/face/aidl/default/Session.h
+++ b/biometrics/face/aidl/default/Session.h
@@ -30,40 +30,38 @@
public:
explicit Session(std::shared_ptr<ISessionCallback> cb);
- ndk::ScopedAStatus generateChallenge(int32_t cookie) override;
+ ndk::ScopedAStatus generateChallenge() override;
- ndk::ScopedAStatus revokeChallenge(int32_t cookie, int64_t challenge) override;
+ ndk::ScopedAStatus revokeChallenge(int64_t challenge) override;
- ndk::ScopedAStatus enroll(int32_t cookie, const keymaster::HardwareAuthToken& hat,
+ ndk::ScopedAStatus enroll(const keymaster::HardwareAuthToken& hat,
EnrollmentType enrollmentType, const std::vector<Feature>& features,
const NativeHandle& previewSurface,
std::shared_ptr<common::ICancellationSignal>* return_val) override;
ndk::ScopedAStatus authenticate(
- int32_t cookie, int64_t keystoreOperationId,
+ int64_t keystoreOperationId,
std::shared_ptr<common::ICancellationSignal>* returnVal) override;
ndk::ScopedAStatus detectInteraction(
- int32_t cookie, std::shared_ptr<common::ICancellationSignal>* returnVal) override;
+ std::shared_ptr<common::ICancellationSignal>* returnVal) override;
- ndk::ScopedAStatus enumerateEnrollments(int32_t cookie) override;
+ ndk::ScopedAStatus enumerateEnrollments() override;
- ndk::ScopedAStatus removeEnrollments(int32_t cookie,
- const std::vector<int32_t>& enrollmentIds) override;
+ ndk::ScopedAStatus removeEnrollments(const std::vector<int32_t>& enrollmentIds) override;
- ndk::ScopedAStatus getFeatures(int32_t cookie, int32_t enrollmentId) override;
+ ndk::ScopedAStatus getFeatures(int32_t enrollmentId) override;
- ndk::ScopedAStatus setFeature(int32_t cookie, const keymaster::HardwareAuthToken& hat,
- int32_t enrollmentId, Feature feature, bool enabled) override;
+ ndk::ScopedAStatus setFeature(const keymaster::HardwareAuthToken& hat, int32_t enrollmentId,
+ Feature feature, bool enabled) override;
- ndk::ScopedAStatus getAuthenticatorId(int32_t cookie) override;
+ ndk::ScopedAStatus getAuthenticatorId() override;
- ndk::ScopedAStatus invalidateAuthenticatorId(int32_t cookie) override;
+ ndk::ScopedAStatus invalidateAuthenticatorId() override;
- ndk::ScopedAStatus resetLockout(int32_t cookie,
- const keymaster::HardwareAuthToken& hat) override;
+ ndk::ScopedAStatus resetLockout(const keymaster::HardwareAuthToken& hat) override;
- ndk::ScopedAStatus close(int32_t cookie) override;
+ ndk::ScopedAStatus close() override;
private:
std::shared_ptr<ISessionCallback> cb_;
diff --git a/biometrics/face/aidl/vts/VtsHalBiometricsFaceTargetTest.cpp b/biometrics/face/aidl/vts/VtsHalBiometricsFaceTargetTest.cpp
index 936fcc6..60e0a2a 100644
--- a/biometrics/face/aidl/vts/VtsHalBiometricsFaceTargetTest.cpp
+++ b/biometrics/face/aidl/vts/VtsHalBiometricsFaceTargetTest.cpp
@@ -21,35 +21,31 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
+#include <chrono>
#include <future>
namespace aidl::android::hardware::biometrics::face {
namespace {
+using namespace std::literals::chrono_literals;
+
constexpr int kSensorId = 0;
constexpr int kUserId = 0;
-constexpr auto kCallbackTimeout = std::chrono::seconds(1);
-enum class SessionCallbackMethodName {
- kOnStateChanged,
+enum class MethodName {
+ kOnError,
+ kOnSessionClosed,
};
-struct SessionCallbackInvocation {
- SessionCallbackMethodName method_name;
- SessionState state;
+struct Invocation {
+ MethodName methodName;
+ Error error;
+ int32_t vendorCode;
};
class SessionCallback : public BnSessionCallback {
public:
- explicit SessionCallback(std::promise<SessionCallbackInvocation> invocation_promise)
- : invocation_promise_(std::move(invocation_promise)) {}
- ndk::ScopedAStatus onStateChanged(int32_t /*cookie*/, SessionState state) override {
- SessionCallbackInvocation invocation = {};
- invocation.method_name = SessionCallbackMethodName::kOnStateChanged;
- invocation.state = state;
- invocation_promise_.set_value(invocation);
- return ndk::ScopedAStatus::ok();
- }
+ explicit SessionCallback(Invocation* inv) : mInv(inv) {}
ndk::ScopedAStatus onChallengeGenerated(int64_t /*challenge*/) override {
return ndk::ScopedAStatus::ok();
@@ -67,7 +63,12 @@
return ndk::ScopedAStatus::ok();
}
- ndk::ScopedAStatus onError(Error /*error*/, int32_t /*vendorCode*/) override {
+ ndk::ScopedAStatus onError(Error error, int32_t vendorCode) override {
+ *mInv = {};
+ mInv->methodName = MethodName::kOnError;
+ mInv->error = error;
+ mInv->vendorCode = vendorCode;
+
return ndk::ScopedAStatus::ok();
}
@@ -120,10 +121,15 @@
return ndk::ScopedAStatus::ok();
}
- ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); }
+ ndk::ScopedAStatus onSessionClosed() override {
+ *mInv = {};
+ mInv->methodName = MethodName::kOnSessionClosed;
+
+ return ndk::ScopedAStatus::ok();
+ }
private:
- std::promise<SessionCallbackInvocation> invocation_promise_;
+ Invocation* mInv;
};
class Face : public testing::TestWithParam<std::string> {
@@ -131,28 +137,34 @@
void SetUp() override {
AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
ASSERT_NE(binder, nullptr);
- hal_ = IFace::fromBinder(ndk::SpAIBinder(binder));
+ mHal = IFace::fromBinder(ndk::SpAIBinder(binder));
}
- std::shared_ptr<IFace> hal_;
+ std::shared_ptr<IFace> mHal;
+ Invocation mInv;
};
TEST_P(Face, AuthenticateTest) {
- std::promise<SessionCallbackInvocation> invocation_promise;
- std::future<SessionCallbackInvocation> invocation_future = invocation_promise.get_future();
- std::shared_ptr<SessionCallback> session_cb =
- ndk::SharedRefBase::make<SessionCallback>(std::move(invocation_promise));
+ // Prepare the callback.
+ auto cb = ndk::SharedRefBase::make<SessionCallback>(&mInv);
+ // Create a session
std::shared_ptr<ISession> session;
- ASSERT_TRUE(hal_->createSession(kSensorId, kUserId, session_cb, &session).isOk());
+ ASSERT_TRUE(mHal->createSession(kSensorId, kUserId, cb, &session).isOk());
- std::shared_ptr<common::ICancellationSignal> cancel_cb;
- ASSERT_TRUE(session->authenticate(0, 0, &cancel_cb).isOk());
- ASSERT_EQ(invocation_future.wait_for(kCallbackTimeout), std::future_status::ready);
+ // Call authenticate
+ std::shared_ptr<common::ICancellationSignal> cancellationSignal;
+ ASSERT_TRUE(session->authenticate(0 /* operationId */, &cancellationSignal).isOk());
- SessionCallbackInvocation invocation = invocation_future.get();
- EXPECT_EQ(invocation.method_name, SessionCallbackMethodName::kOnStateChanged);
- EXPECT_EQ(invocation.state, SessionState::AUTHENTICATING);
+ // Get the results
+ EXPECT_EQ(mInv.methodName, MethodName::kOnError);
+ EXPECT_EQ(mInv.error, Error::UNABLE_TO_PROCESS);
+ EXPECT_EQ(mInv.vendorCode, 0);
+
+ // Close the session
+ ASSERT_TRUE(session->close().isOk());
+
+ EXPECT_EQ(mInv.methodName, MethodName::kOnSessionClosed);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Face);
@@ -161,6 +173,7 @@
::android::PrintInstanceNameToString);
} // namespace
+} // namespace aidl::android::hardware::biometrics::face
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
@@ -169,4 +182,3 @@
return RUN_ALL_TESTS();
}
-} // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/AcquiredInfo.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/AcquiredInfo.aidl
index a8e73fc..2c2011a 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/AcquiredInfo.aidl
+++ b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/AcquiredInfo.aidl
@@ -45,4 +45,5 @@
TOO_DARK = 8,
TOO_BRIGHT = 9,
IMMOBILE = 10,
+ RETRYING_CAPTURE = 11,
}
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
index 87eaf96..9934a76 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
+++ b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
@@ -34,17 +34,17 @@
package android.hardware.biometrics.fingerprint;
@VintfStability
interface ISession {
- void generateChallenge(in int cookie);
- void revokeChallenge(in int cookie, in long challenge);
- android.hardware.biometrics.common.ICancellationSignal enroll(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat);
- android.hardware.biometrics.common.ICancellationSignal authenticate(in int cookie, in long operationId);
- android.hardware.biometrics.common.ICancellationSignal detectInteraction(in int cookie);
- void enumerateEnrollments(in int cookie);
- void removeEnrollments(in int cookie, in int[] enrollmentIds);
- void getAuthenticatorId(in int cookie);
- void invalidateAuthenticatorId(in int cookie);
- void resetLockout(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat);
- void close(in int cookie);
+ void generateChallenge();
+ void revokeChallenge(in long challenge);
+ android.hardware.biometrics.common.ICancellationSignal enroll(in android.hardware.keymaster.HardwareAuthToken hat);
+ android.hardware.biometrics.common.ICancellationSignal authenticate(in long operationId);
+ android.hardware.biometrics.common.ICancellationSignal detectInteraction();
+ void enumerateEnrollments();
+ void removeEnrollments(in int[] enrollmentIds);
+ void getAuthenticatorId();
+ void invalidateAuthenticatorId();
+ void resetLockout(in android.hardware.keymaster.HardwareAuthToken hat);
+ void close();
void onPointerDown(in int pointerId, in int x, in int y, in float minor, in float major);
void onPointerUp(in int pointerId);
void onUiReady();
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISessionCallback.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISessionCallback.aidl
index 3a97717..3c40ad6 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISessionCallback.aidl
+++ b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISessionCallback.aidl
@@ -34,7 +34,6 @@
package android.hardware.biometrics.fingerprint;
@VintfStability
interface ISessionCallback {
- void onStateChanged(in int cookie, in android.hardware.biometrics.fingerprint.SessionState state);
void onChallengeGenerated(in long challenge);
void onChallengeRevoked(in long challenge);
void onAcquired(in android.hardware.biometrics.fingerprint.AcquiredInfo info, in int vendorCode);
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/SessionState.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/SessionState.aidl
deleted file mode 100644
index 9b0b6f6..0000000
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/SessionState.aidl
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-// the interface (from the latest frozen version), the build system will
-// prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.biometrics.fingerprint;
-@Backing(type="byte") @VintfStability
-enum SessionState {
- IDLING = 0,
- CLOSED = 1,
- GENERATING_CHALLENGE = 2,
- REVOKING_CHALLENGE = 3,
- ENROLLING = 4,
- AUTHENTICATING = 5,
- DETECTING_INTERACTION = 6,
- ENUMERATING_ENROLLMENTS = 7,
- REMOVING_ENROLLMENTS = 8,
- GETTING_AUTHENTICATOR_ID = 9,
- INVALIDATING_AUTHENTICATOR_ID = 10,
- RESETTING_LOCKOUT = 11,
-}
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfo.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfo.aidl
index bf11daa..b406947 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfo.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfo.aidl
@@ -82,5 +82,11 @@
* same finger can help against false rejections.
*/
IMMOBILE,
-}
+ /**
+ * This message may be sent to notify the framework that an additional image capture is taking
+ * place. Multiple RETRYING_CAPTURE may be sent before an ACQUIRED_GOOD message is sent.
+ * However, RETRYING_CAPTURE must not be sent after ACQUIRED_GOOD is sent.
+ */
+ RETRYING_CAPTURE,
+}
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IFingerprint.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IFingerprint.aidl
index 98a4530..271a9bf 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IFingerprint.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IFingerprint.aidl
@@ -32,27 +32,14 @@
/**
* createSession:
*
- * Creates a session which can then be used by the framework to perform operations such as
- * enroll, authenticate, etc for the given sensorId and userId.
+ * Creates a instance of ISession which can be used by the framework to perform operations
+ * such as ISession#enroll, ISession#authenticate, etc. for the given sensorId and userId.
*
- * Calling this method while there is an active session is considered an error. If the
- * framework is in a bad state and for some reason cannot close its session, it should use
- * the reset method below.
- *
- * A physical sensor identified by sensorId typically supports only a single in-flight session
- * at a time. As such, if a session is currently in a state other than SessionState::IDLING, the
- * HAL MUST finish or cancel the current operation and return to SessionState::IDLING before the
- * new session is created. For example:
- * 1) If a session for sensorId=0, userId=0 is currently in a cancellable state (see
- * ICancellationSignal) such as SessionState::AUTHENTICATING and the framework requests a
- * new session for sensorId=0, userId=10, the HAL must end the current session with
- * Error::CANCELED, invoke ISessionCallback#onStateChanged with SessionState::IDLING, and
- * then return a new session for sensorId=0, userId=10.
- * 2) If a session for sensorId=0, userId=0 is currently in a non-cancellable state such as
- * SessionState::REMOVING_ENROLLMENTS, and the framework requests a new session for
- * sensorId=0, userId=10, the HAL must finish the current operation before invoking
- * ISessionCallback#onStateChanged with SessionState::IDLING, and return a new session for
- * sensorId=0, userId=10.
+ * Calling this method while there is an active session is considered an error. If the framework
+ * wants to create a new session when it already has an active session, it must first cancel the
+ * current operation if it's cancellable, or wait until it completes. Then, the framework must
+ * explicitly close the session with ISession#close. Once the framework receives
+ * ISessionCallback#onSessionClosed, a new session can be created.
*
* Implementations must store user-specific state or metadata in /data/vendor_de/<user>/fpdata
* as specified by the SeLinux policy. This directory is created/removed by vold (see
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
index ef2e6fc..940548b 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
@@ -22,23 +22,28 @@
/**
* Operations that can be performed for unique sessions retrieved via IFingerprint#createSession.
* Methods defined within this interface can be split into the following categories:
- * 1) Methods associated with a state (see the SessionState enum). State-based operations are
- * handled by the HAL in FIFO order.
- * 1a) Cancellable state-based operations. If a cancellable operation is in-progress and the
- * framework requests a subsequent state-based operation, the implementation should finish
- * the operation via ISessionCallback#onError with Error::CANCELED.
- * 1b) Non-cancellable state-based operations. These operations should fully complete before the
- * next state-based operation can be started.
- * 2) Methods without a state. These methods may be invoked by the framework depending on its
- * use case. For example on devices with sensors of FingerprintSensorType::UNDER_DISPLAY_*,
- * ISession#onFingerDown may be invoked while the HAL is in SessionState::ENROLLING,
- * SessionState::AUTHENTICATING, or SessionState::DETECTING_INTERACTION.
+ * 1) Non-interrupting operations. These operations are handled by the HAL in FIFO order.
+ * 1a) Cancellable operations. These are usually the operations that can execute for several
+ * minutes. To allow for cancellation, they return an instance of ICancellationSignal that
+ * lets the framework cancel them by calling ICancellationSignal#cancel. If such an operation
+ * is cancelled, it must notify the framework by calling ISessionCallback#onError with
+ * Error::CANCELED.
+ * 1b) Non-cancellable operations. Such operations cannot be cancelled once started.
+ * 2) Interrupting operations. These operations may be invoked by the framework immediately,
+ * regardless of whether another operation is executing. For example, on devices with sensors
+ * of FingerprintSensorType::UNDER_DISPLAY_*, ISession#onFingerDown may be invoked while the
+ * HAL is executing ISession#enroll, ISession#authenticate or ISession#detectInteraction.
*
- * If the HAL has multiple operations in its queue, it is not required to notify the framework
- * of SessionState::IDLING between each operation. However, it must notify the framework when all
- * work is completed. See ISessionCallback#onStateChanged. For example, the following is a valid
- * sequence of ISessionCallback#onStateChanged invocations: SessionState::IDLING -->
- * SessionState::ENROLLING --> SessionState::ENUMERATING_ENROLLMENTS --> SessionState::IDLING.
+ * The lifecycle of a non-interrupting operation ends when one of its terminal callbacks is called.
+ * For example, ISession#authenticate is considered completed when either of the following callbacks
+ * is called: ISessionCallback#onError or ISessionCallback#onAuthenticationSucceeded.
+ *
+ * The lifecycle of an interrupting operation ends when it returns. Interrupting operations do not
+ * have callbacks.
+ *
+ * ISession only supports execution of one non-interrupting operation at a time, regardless of
+ * whether it's cancellable. The framework must wait for a corresponding callback indicating the end of
+ * the current non-interrupting operation before a new non-interrupting operation can be started.
*/
@VintfStability
interface ISession {
@@ -84,9 +89,8 @@
* | 0 | 10 | <Time4> | <Random4> |
* ----------------------------------------------
*
- * @param cookie A unique number identifying this operation
*/
- void generateChallenge(in int cookie);
+ void generateChallenge();
/**
* revokeChallenge:
@@ -95,23 +99,17 @@
* parameters is requested, the implementation must still notify the framework using the
* provided callback.
*
- * @param cookie A unique number identifying this operation
* @param challenge Challenge that should be revoked.
*/
- void revokeChallenge(in int cookie, in long challenge);
+ void revokeChallenge(in long challenge);
/**
* enroll:
*
* A request to add a fingerprint enrollment.
*
- * Once the HAL is able to start processing the enrollment request, it must notify the framework
- * via ISessionCallback#onStateChanged with SessionState::ENROLLING.
- *
* At any point during enrollment, if a non-recoverable error occurs, the HAL must notify the
- * framework via ISessionCallback#onError with the applicable enrollment-specific error, and
- * then send ISessionCallback#onStateChanged(cookie, SessionState::IDLING) if no subsequent
- * operation is in the queue.
+ * framework via ISessionCallback#onError with the applicable enrollment-specific error.
*
* Before capturing fingerprint data, the implementation must first verify the authenticity and
* integrity of the provided HardwareAuthToken. In addition, it must check that the challenge
@@ -132,24 +130,17 @@
* implementation MUST update and associate this (sensorId, userId) pair with a new new
* entropy-encoded random identifier. See ISession#getAuthenticatorId for more information.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
* @param hat See above documentation.
*/
- ICancellationSignal enroll(in int cookie, in HardwareAuthToken hat);
+ ICancellationSignal enroll(in HardwareAuthToken hat);
/**
* authenticate:
*
* A request to start looking for fingerprints to authenticate.
*
- * Once the HAL is able to start processing the authentication request, it must notify framework
- * via ISessionCallback#onStateChanged with SessionState::AUTHENTICATING.
- *
* At any point during authentication, if a non-recoverable error occurs, the HAL must notify
- * the framework via ISessionCallback#onError with the applicable authentication-specific error,
- * and then send ISessionCallback#onStateChanged(cookie, SessionState::IDLING) if no
- * subsequent operation is in the queue.
+ * the framework via ISessionCallback#onError with the applicable authentication-specific error.
*
* During authentication, the implementation may notify the framework via
* ISessionCallback#onAcquired with messages that may be used to guide the user. This callback
@@ -171,8 +162,6 @@
* must be set with the operationId passed in during #authenticate. If the sensor is NOT
* SensorStrength::STRONG, the HardwareAuthToken MUST be null.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
* @param operationId For sensors configured as SensorStrength::STRONG, this must be used ONLY
* upon successful authentication and wrapped in the HardwareAuthToken's
* "challenge" field and sent to the framework via
@@ -184,7 +173,7 @@
* setUserAuthenticationParameters in KeyGenParameterSpec.Builder and
* KeyProtection.Builder.
*/
- ICancellationSignal authenticate(in int cookie, in long operationId);
+ ICancellationSignal authenticate(in long operationId);
/**
* detectInteraction:
@@ -193,17 +182,12 @@
* if SensorProps#supportsDetectInteraction is true. If invoked on implementations that do not
* support this functionality, the HAL must respond with ISession#onError(UNABLE_TO_PROCESS, 0).
*
- * Once the HAL is able to start processing this request, it must notify the framework via
- * ISessionCallback#onStateChanged with SessionState::DETECTING_INTERACTION.
- *
* The framework will use this method in cases where determing user presence is required, but
* identifying/authentication is not. For example, when the device is encrypted (first boot) or
* in lockdown mode.
*
* At any point during detectInteraction, if a non-recoverable error occurs, the HAL must notify
- * the framework via ISessionCallback#onError with the applicable error, and then send
- * ISessionCallback#onStateChanged(cookie, SessionState::IDLING) if no subsequent operation is
- * in the queue.
+ * the framework via ISessionCallback#onError with the applicable error.
*
* The implementation must only check for a fingerprint-like image was detected (e.g. to
* minimize interactions due to non-fingerprint objects), and the lockout counter must not
@@ -221,10 +205,8 @@
* Note that if the operation is canceled, the implementation must notify the framework via
* ISessionCallback#onError with Error::CANCELED.
*
- * @param cookie An identifier used to track subsystem operations related to this call path.
- * The framework will guarantee that it is unique per ISession.
*/
- ICancellationSignal detectInteraction(in int cookie);
+ ICancellationSignal detectInteraction();
/*
* enumerateEnrollments:
@@ -232,32 +214,22 @@
* A request to enumerate (list) the enrollments for this (sensorId, userId) pair. The
* framework typically uses this to ensure that its cache is in sync with the HAL.
*
- * Once the HAL is able to start processing this request, it must notify the framework via
- * ISessionCallback#onStateChanged with SessionState::ENUMERATING_ENROLLMENTS.
- *
* The implementation must then notify the framework with a list of enrollments applicable
* for the current session via ISessionCallback#onEnrollmentsEnumerated.
*
- * @param cookie An identifier used to track subsystem operations related to this call path.
- * The framework will guarantee that it is unique per ISession.
*/
- void enumerateEnrollments(in int cookie);
+ void enumerateEnrollments();
/**
* removeEnrollments:
*
* A request to remove the enrollments for this (sensorId, userId) pair.
*
- * Once the HAL is able to start processing this request, it must notify the framework via
- * ISessionCallback#onStateChanged with SessionState::REMOVING_ENROLLMENTS.
- *
* After removing the enrollmentIds from everywhere necessary (filesystem, secure subsystems,
* etc), the implementation must notify the framework via ISessionCallback#onEnrollmentsRemoved.
*
- * @param cookie An identifier used to track subsystem operations related to this call path.
- * The framework will guarantee that it is unique per ISession.
*/
- void removeEnrollments(in int cookie, in int[] enrollmentIds);
+ void removeEnrollments(in int[] enrollmentIds);
/**
* getAuthenticatorId:
@@ -285,10 +257,8 @@
* 3) MUST not change if a fingerprint is deleted.
* 4) MUST be an entropy-encoded random number
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
*/
- void getAuthenticatorId(in int cookie);
+ void getAuthenticatorId();
/**
* invalidateAuthenticatorId:
@@ -312,10 +282,8 @@
* for more details). As such, the framework would coordinate invalidation across multiple
* biometric HALs as necessary.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
*/
- void invalidateAuthenticatorId(in int cookie);
+ void invalidateAuthenticatorId();
/**
* resetLockout:
@@ -326,8 +294,7 @@
* 2) Verify that the timestamp provided within the HAT is relatively recent (e.g. on the
* order of minutes, not hours).
* If either of the checks fail, the HAL must invoke ISessionCallback#onError with
- * Error::UNABLE_TO_PROCESS and return to SessionState::IDLING if no subsequent work is in the
- * queue.
+ * Error::UNABLE_TO_PROCESS and return to the idling state.
*
* Upon successful verification, the HAL must clear the lockout counter and notify the framework
* via ISessionCallback#onLockoutCleared.
@@ -358,29 +325,26 @@
* See the Android CDD section 7.3.10 for the full set of lockout and rate-limiting
* requirements.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
* @param hat HardwareAuthToken See above documentation.
*/
- void resetLockout(in int cookie, in HardwareAuthToken hat);
+ void resetLockout(in HardwareAuthToken hat);
/*
* Close this session and allow the HAL to release the resources associated with this session.
*
- * A session can only be closed when it's in SessionState::IDLING. Closing a session will
- * result in a ISessionCallback#onStateChanged call with SessionState::CLOSED.
+ * A session can only be closed when the HAL is idling, i.e. not performing any of the
+ * non-interruptable operations. If the HAL is busy performing a cancellable operation, the
+ * operation must be explicitly cancelled with a call to ICancellationSignal#cancel before
+ * the session can be closed.
*
- * If a session is unresponsive or stuck in a state other than SessionState::CLOSED,
- * IFingerprint#reset could be used as a last resort to terminate the session and recover the
- * HAL from a bad state.
+ * After a session is closed, the HAL must notify the framework by calling
+ * ISessionCallback#onSessionClosed.
*
* All sessions must be explicitly closed. Calling IFingerprint#createSession while there is an
* active session is considered an error.
*
- * @param cookie An identifier used to track subsystem operations related to this call path. The
- * client must guarantee that it is unique per ISession.
*/
- void close(in int cookie);
+ void close();
/**
* Methods for notifying the under-display fingerprint sensor about external events.
@@ -394,9 +358,8 @@
* of other types, the HAL must treat this as a no-op and return immediately.
*
* For sensors of type FingerprintSensorType::UNDER_DISPLAY_*, this method is used to notify the
- * HAL of display touches. This method can be invoked when the session is in one of the
- * following states: SessionState::ENROLLING, SessionState::AUTHENTICATING, or
- * SessionState::DETECTING_INTERACTION.
+ * HAL of display touches. This method can be invoked when the HAL is performing any one of:
+ * ISession#authenticate, ISession#enroll, ISession#detectInteraction.
*
* Note that the framework will only invoke this method if the event occurred on the display on
* which this sensor is located.
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISessionCallback.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISessionCallback.aidl
index cf3a271..95657b3 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISessionCallback.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISessionCallback.aidl
@@ -18,17 +18,11 @@
import android.hardware.biometrics.fingerprint.AcquiredInfo;
import android.hardware.biometrics.fingerprint.Error;
-import android.hardware.biometrics.fingerprint.SessionState;
import android.hardware.keymaster.HardwareAuthToken;
@VintfStability
interface ISessionCallback {
/**
- * Used to notify the framework of session state changes. See ISession for more information.
- */
- void onStateChanged(in int cookie, in SessionState state);
-
- /**
* Notifies the framework when a challenge is successfully generated.
*/
void onChallengeGenerated(in long challenge);
@@ -39,10 +33,10 @@
void onChallengeRevoked(in long challenge);
/**
- * This method must only be used to notify the framework during the following states:
- * 1) SessionState::ENROLLING
- * 2) SessionState::AUTHENTICATING
- * 3) SessionState::DETECTING_INTERACTION
+ * This method must only be used to notify the framework during the following operations:
+ * 1) ISession#enroll
+ * 2) ISession#authenticate
+ * 3) ISession#detectInteraction
*
* These messages may be used to provide user guidance multiple times if necessary per
* operation.
@@ -56,18 +50,18 @@
void onAcquired(in AcquiredInfo info, in int vendorCode);
/**
- * This method must only be used to notify the framework during the following states:
- * 1) SessionState::ENROLLING
- * 2) SessionState::AUTHENTICATING
- * 3) SessionState::DETECTING_INTERACTION
- * 4) SessionState::INVALIDATING_AUTHENTICATOR_ID
- * 5) SessionState::RESETTING_LOCKOUT
+ * This method must only be used to notify the framework during the following operations:
+ * 1) ISession#enroll
+ * 2) ISession#authenticate
+ * 3) ISession#detectInteraction
+ * 4) ISession#invalidateAuthenticatorId
+ * 5) ISession#resetLockout
*
* These messages may be used to notify the framework or user that a non-recoverable error
- * has occurred. The operation is finished, and the HAL must proceed with the next operation
- * or return to SessionState::IDLING if the queue is empty.
+ * has occurred. The operation is finished, and the HAL can proceed with the next operation
+ * or return to the idling state.
*
- * Note that cancellation (see common::ICancellationSignal) and preemption most be followed with
+ * Note that cancellation (see common::ICancellationSignal) and preemption must be followed with
* an Error::CANCELED message.
*
* @param error See the Error enum.
@@ -79,8 +73,7 @@
void onError(in Error error, in int vendorCode);
/**
- * This method must only be used to notify the framework during the following state:
- * 1) SessionState::ENROLLING
+ * This method must only be used to notify the framework during the ISession#enroll operation.
*
* @param enrollmentId Unique stable identifier for the enrollment that's being added by this
* ISession#enroll invocation.
@@ -89,7 +82,7 @@
void onEnrollmentProgress(in int enrollmentId, int remaining);
/**
- * This method must only be used to notify the framework during SessionState::AUTHENTICATING.
+ * This method must only be used to notify the framework during ISession#authenticate.
*
* Used to notify the framework upon successful authentication. Note that the authentication
* lifecycle ends when either 1) a fingerprint is accepted, or 2) an error occurred. The
@@ -104,7 +97,7 @@
void onAuthenticationSucceeded(in int enrollmentId, in HardwareAuthToken hat);
/**
- * This method must only be used to notify the framework during SessionState::AUTHENTICATING.
+ * This method must only be used to notify the framework during ISession#authenticate.
*
* Used to notify the framework upon rejected attempts. Note that the authentication
* lifecycle ends when either 1) a fingerprint is accepted, or 2) an occurred. The
@@ -113,7 +106,7 @@
void onAuthenticationFailed();
/**
- * This method must only be used to notify the framework during SessionState::AUTHENTICATING.
+ * This method must only be used to notify the framework during ISession#authenticate.
*
* Authentication is locked out due to too many unsuccessful attempts. This is a rate-limiting
* lockout, and authentication can be restarted after a period of time. See
@@ -126,7 +119,7 @@
void onLockoutTimed(in long durationMillis);
/**
- * This method must only be used to notify the framework during SessionState::AUTHENTICATING.
+ * This method must only be used to notify the framework during ISession#authenticate.
*
* Authentication is disabled until the user unlocks with their device credential
* (PIN/Pattern/Password). See ISession#resetLockout.
@@ -153,7 +146,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::DETECTING_INTERACTION
+ * ISession#detectInteraction
*
* Notifies the framework that user interaction occurred. See ISession#detectInteraction.
*/
@@ -161,7 +154,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::ENUMERATING_ENROLLMENTS.
+ * ISession#enumerateEnrollments.
*
* Notifies the framework of the current enrollments. See ISession#enumerateEnrollments.
*
@@ -171,7 +164,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::REMOVING_ENROLLMENTS.
+ * ISession#removeEnrollments.
*
* Notifies the framework that the specified enrollments are removed.
*
@@ -181,7 +174,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::GETTING_AUTHENTICATOR_ID.
+ * ISession#getAuthenticatorId.
*
* Notifies the framework with the authenticatorId corresponding to this session's
* (userId, sensorId) pair.
@@ -192,7 +185,7 @@
/**
* This method must only be used to notify the framework during
- * SessionState::INVALIDATING_AUTHENTICATOR_ID.
+ * ISession#invalidateAuthenticatorId.
*
* See ISession#invalidateAuthenticatorId for more information.
*
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/SessionState.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/SessionState.aidl
deleted file mode 100644
index 19a6ce3..0000000
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/SessionState.aidl
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.biometrics.fingerprint;
-
-@VintfStability
-@Backing(type="byte")
-enum SessionState {
- /**
- * The HAL is not processing any session requests.
- */
- IDLING,
-
- /**
- * The session has been closed by the client.
- */
- CLOSED,
-
- /**
- * The HAL is processing the ISession#generateChallenge request.
- */
- GENERATING_CHALLENGE,
-
- /**
- * The HAL is processing the ISession#revokeChallenge request.
- */
- REVOKING_CHALLENGE,
-
- /**
- * The HAL is processing the ISession#enroll request.
- */
- ENROLLING,
-
- /**
- * The HAL is processing the ISession#authenticate request.
- */
- AUTHENTICATING,
-
- /**
- * The HAL is processing the ISession#detectInteraction request.
- */
- DETECTING_INTERACTION,
-
- /**
- * The HAL is processing the ISession#enumerateEnrollments request.
- */
- ENUMERATING_ENROLLMENTS,
-
- /**
- * The HAL is processing the ISession#removeEnrollments request.
- */
- REMOVING_ENROLLMENTS,
-
- /**
- * The HAL is processing the ISession#getAuthenticatorId request.
- */
- GETTING_AUTHENTICATOR_ID,
-
- /**
- * The HAL is processing the ISession#invalidateAuthenticatorId request.
- */
- INVALIDATING_AUTHENTICATOR_ID,
-
- /**
- * The HAL is processing the ISession#resetLockout request.
- */
- RESETTING_LOCKOUT
-}
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index f030f13..ca481e7 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -39,54 +39,56 @@
}
void Session::scheduleStateOrCrash(SessionState state) {
- CHECK(mScheduledState == SessionState::IDLING);
- CHECK(mCurrentState == SessionState::IDLING);
+ // TODO(b/166800618): call enterIdling from the terminal callbacks and restore these checks.
+ // CHECK(mScheduledState == SessionState::IDLING);
+ // CHECK(mCurrentState == SessionState::IDLING);
mScheduledState = state;
}
-void Session::enterStateOrCrash(int cookie, SessionState state) {
+void Session::enterStateOrCrash(SessionState state) {
CHECK(mScheduledState == state);
mCurrentState = state;
mScheduledState = SessionState::IDLING;
- mCb->onStateChanged(cookie, mCurrentState);
}
-void Session::enterIdling(int cookie) {
- mCurrentState = SessionState::IDLING;
- mCb->onStateChanged(cookie, mCurrentState);
+void Session::enterIdling() {
+ // TODO(b/166800618): call enterIdling from the terminal callbacks and rethink this conditional.
+ if (mCurrentState != SessionState::CLOSED) {
+ mCurrentState = SessionState::IDLING;
+ }
}
bool Session::isClosed() {
return mCurrentState == SessionState::CLOSED;
}
-ndk::ScopedAStatus Session::generateChallenge(int32_t cookie) {
+ndk::ScopedAStatus Session::generateChallenge() {
LOG(INFO) << "generateChallenge";
scheduleStateOrCrash(SessionState::GENERATING_CHALLENGE);
- mWorker->schedule(Callable::from([this, cookie] {
- enterStateOrCrash(cookie, SessionState::GENERATING_CHALLENGE);
+ mWorker->schedule(Callable::from([this] {
+ enterStateOrCrash(SessionState::GENERATING_CHALLENGE);
mEngine->generateChallengeImpl(mCb.get());
- enterIdling(cookie);
+ enterIdling();
}));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::revokeChallenge(int32_t cookie, int64_t challenge) {
+ndk::ScopedAStatus Session::revokeChallenge(int64_t challenge) {
LOG(INFO) << "revokeChallenge";
scheduleStateOrCrash(SessionState::REVOKING_CHALLENGE);
- mWorker->schedule(Callable::from([this, cookie, challenge] {
- enterStateOrCrash(cookie, SessionState::REVOKING_CHALLENGE);
+ mWorker->schedule(Callable::from([this, challenge] {
+ enterStateOrCrash(SessionState::REVOKING_CHALLENGE);
mEngine->revokeChallengeImpl(mCb.get(), challenge);
- enterIdling(cookie);
+ enterIdling();
}));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::enroll(int32_t cookie, const keymaster::HardwareAuthToken& hat,
+ndk::ScopedAStatus Session::enroll(const keymaster::HardwareAuthToken& hat,
std::shared_ptr<common::ICancellationSignal>* out) {
LOG(INFO) << "enroll";
scheduleStateOrCrash(SessionState::ENROLLING);
@@ -94,21 +96,21 @@
std::promise<void> cancellationPromise;
auto cancFuture = cancellationPromise.get_future();
- mWorker->schedule(Callable::from([this, cookie, hat, cancFuture = std::move(cancFuture)] {
- enterStateOrCrash(cookie, SessionState::ENROLLING);
+ mWorker->schedule(Callable::from([this, hat, cancFuture = std::move(cancFuture)] {
+ enterStateOrCrash(SessionState::ENROLLING);
if (shouldCancel(cancFuture)) {
mCb->onError(Error::CANCELED, 0 /* vendorCode */);
} else {
mEngine->enrollImpl(mCb.get(), hat);
}
- enterIdling(cookie);
+ enterIdling();
}));
*out = SharedRefBase::make<CancellationSignal>(std::move(cancellationPromise));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::authenticate(int32_t cookie, int64_t operationId,
+ndk::ScopedAStatus Session::authenticate(int64_t operationId,
std::shared_ptr<common::ICancellationSignal>* out) {
LOG(INFO) << "authenticate";
scheduleStateOrCrash(SessionState::AUTHENTICATING);
@@ -116,112 +118,111 @@
std::promise<void> cancPromise;
auto cancFuture = cancPromise.get_future();
- mWorker->schedule(
- Callable::from([this, cookie, operationId, cancFuture = std::move(cancFuture)] {
- enterStateOrCrash(cookie, SessionState::AUTHENTICATING);
- if (shouldCancel(cancFuture)) {
- mCb->onError(Error::CANCELED, 0 /* vendorCode */);
- } else {
- mEngine->authenticateImpl(mCb.get(), operationId);
- }
- enterIdling(cookie);
- }));
+ mWorker->schedule(Callable::from([this, operationId, cancFuture = std::move(cancFuture)] {
+ enterStateOrCrash(SessionState::AUTHENTICATING);
+ if (shouldCancel(cancFuture)) {
+ mCb->onError(Error::CANCELED, 0 /* vendorCode */);
+ } else {
+ mEngine->authenticateImpl(mCb.get(), operationId);
+ }
+ enterIdling();
+ }));
*out = SharedRefBase::make<CancellationSignal>(std::move(cancPromise));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::detectInteraction(int32_t cookie,
- std::shared_ptr<common::ICancellationSignal>* out) {
+ndk::ScopedAStatus Session::detectInteraction(std::shared_ptr<common::ICancellationSignal>* out) {
LOG(INFO) << "detectInteraction";
scheduleStateOrCrash(SessionState::DETECTING_INTERACTION);
std::promise<void> cancellationPromise;
auto cancFuture = cancellationPromise.get_future();
- mWorker->schedule(Callable::from([this, cookie, cancFuture = std::move(cancFuture)] {
- enterStateOrCrash(cookie, SessionState::DETECTING_INTERACTION);
+ mWorker->schedule(Callable::from([this, cancFuture = std::move(cancFuture)] {
+ enterStateOrCrash(SessionState::DETECTING_INTERACTION);
if (shouldCancel(cancFuture)) {
mCb->onError(Error::CANCELED, 0 /* vendorCode */);
} else {
mEngine->detectInteractionImpl(mCb.get());
}
- enterIdling(cookie);
+ enterIdling();
}));
*out = SharedRefBase::make<CancellationSignal>(std::move(cancellationPromise));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::enumerateEnrollments(int32_t cookie) {
+ndk::ScopedAStatus Session::enumerateEnrollments() {
LOG(INFO) << "enumerateEnrollments";
scheduleStateOrCrash(SessionState::ENUMERATING_ENROLLMENTS);
- mWorker->schedule(Callable::from([this, cookie] {
- enterStateOrCrash(cookie, SessionState::ENUMERATING_ENROLLMENTS);
+ mWorker->schedule(Callable::from([this] {
+ enterStateOrCrash(SessionState::ENUMERATING_ENROLLMENTS);
mEngine->enumerateEnrollmentsImpl(mCb.get());
- enterIdling(cookie);
+ enterIdling();
}));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::removeEnrollments(int32_t cookie,
- const std::vector<int32_t>& enrollmentIds) {
+ndk::ScopedAStatus Session::removeEnrollments(const std::vector<int32_t>& enrollmentIds) {
LOG(INFO) << "removeEnrollments";
scheduleStateOrCrash(SessionState::REMOVING_ENROLLMENTS);
- mWorker->schedule(Callable::from([this, cookie, enrollmentIds] {
- enterStateOrCrash(cookie, SessionState::REMOVING_ENROLLMENTS);
+ mWorker->schedule(Callable::from([this, enrollmentIds] {
+ enterStateOrCrash(SessionState::REMOVING_ENROLLMENTS);
mEngine->removeEnrollmentsImpl(mCb.get(), enrollmentIds);
- enterIdling(cookie);
+ enterIdling();
}));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::getAuthenticatorId(int32_t cookie) {
+ndk::ScopedAStatus Session::getAuthenticatorId() {
LOG(INFO) << "getAuthenticatorId";
scheduleStateOrCrash(SessionState::GETTING_AUTHENTICATOR_ID);
- mWorker->schedule(Callable::from([this, cookie] {
- enterStateOrCrash(cookie, SessionState::GETTING_AUTHENTICATOR_ID);
+ mWorker->schedule(Callable::from([this] {
+ enterStateOrCrash(SessionState::GETTING_AUTHENTICATOR_ID);
mEngine->getAuthenticatorIdImpl(mCb.get());
- enterIdling(cookie);
+ enterIdling();
}));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::invalidateAuthenticatorId(int32_t cookie) {
+ndk::ScopedAStatus Session::invalidateAuthenticatorId() {
LOG(INFO) << "invalidateAuthenticatorId";
scheduleStateOrCrash(SessionState::INVALIDATING_AUTHENTICATOR_ID);
- mWorker->schedule(Callable::from([this, cookie] {
- enterStateOrCrash(cookie, SessionState::INVALIDATING_AUTHENTICATOR_ID);
+ mWorker->schedule(Callable::from([this] {
+ enterStateOrCrash(SessionState::INVALIDATING_AUTHENTICATOR_ID);
mEngine->invalidateAuthenticatorIdImpl(mCb.get());
- enterIdling(cookie);
+ enterIdling();
}));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::resetLockout(int32_t cookie, const keymaster::HardwareAuthToken& hat) {
+ndk::ScopedAStatus Session::resetLockout(const keymaster::HardwareAuthToken& hat) {
LOG(INFO) << "resetLockout";
scheduleStateOrCrash(SessionState::RESETTING_LOCKOUT);
- mWorker->schedule(Callable::from([this, cookie, hat] {
- enterStateOrCrash(cookie, SessionState::RESETTING_LOCKOUT);
+ mWorker->schedule(Callable::from([this, hat] {
+ enterStateOrCrash(SessionState::RESETTING_LOCKOUT);
mEngine->resetLockoutImpl(mCb.get(), hat);
- enterIdling(cookie);
+ enterIdling();
}));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::close(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::close() {
LOG(INFO) << "close";
- CHECK(mCurrentState == SessionState::IDLING) << "Can't close a non-idling session. Crashing.";
+ // TODO(b/166800618): call enterIdling from the terminal callbacks and restore this check.
+ // CHECK(mCurrentState == SessionState::IDLING) << "Can't close a non-idling session.
+ // Crashing.";
mCurrentState = SessionState::CLOSED;
mCb->onSessionClosed();
return ndk::ScopedAStatus::ok();
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index 42e1aa5..6667f7a 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -37,7 +37,7 @@
cb->onEnrollmentProgress(0 /* enrollmentId */, 0 /* remaining */);
}
- void authenticateImpl(ISessionCallback* cb, int64_t /*operationId*/) {
+ void authenticateImpl(ISessionCallback* cb, int64_t /* operationId */) {
LOG(INFO) << "authenticateImpl";
cb->onAuthenticationSucceeded(0 /* enrollmentId */, {} /* hat */);
}
diff --git a/biometrics/fingerprint/aidl/default/include/Session.h b/biometrics/fingerprint/aidl/default/include/Session.h
index 97d5645..9e46422 100644
--- a/biometrics/fingerprint/aidl/default/include/Session.h
+++ b/biometrics/fingerprint/aidl/default/include/Session.h
@@ -27,37 +27,50 @@
namespace common = aidl::android::hardware::biometrics::common;
namespace keymaster = aidl::android::hardware::keymaster;
+enum class SessionState {
+ IDLING,
+ CLOSED,
+ GENERATING_CHALLENGE,
+ REVOKING_CHALLENGE,
+ ENROLLING,
+ AUTHENTICATING,
+ DETECTING_INTERACTION,
+ ENUMERATING_ENROLLMENTS,
+ REMOVING_ENROLLMENTS,
+ GETTING_AUTHENTICATOR_ID,
+ INVALIDATING_AUTHENTICATOR_ID,
+ RESETTING_LOCKOUT,
+};
+
class Session : public BnSession {
public:
Session(int sensorId, int userId, std::shared_ptr<ISessionCallback> cb,
FakeFingerprintEngine* engine, WorkerThread* worker);
- ndk::ScopedAStatus generateChallenge(int32_t cookie) override;
+ ndk::ScopedAStatus generateChallenge() override;
- ndk::ScopedAStatus revokeChallenge(int32_t cookie, int64_t challenge) override;
+ ndk::ScopedAStatus revokeChallenge(int64_t challenge) override;
- ndk::ScopedAStatus enroll(int32_t cookie, const keymaster::HardwareAuthToken& hat,
+ ndk::ScopedAStatus enroll(const keymaster::HardwareAuthToken& hat,
std::shared_ptr<common::ICancellationSignal>* out) override;
- ndk::ScopedAStatus authenticate(int32_t cookie, int64_t operationId,
+ ndk::ScopedAStatus authenticate(int64_t operationId,
std::shared_ptr<common::ICancellationSignal>* out) override;
ndk::ScopedAStatus detectInteraction(
- int32_t cookie, std::shared_ptr<common::ICancellationSignal>* out) override;
+ std::shared_ptr<common::ICancellationSignal>* out) override;
- ndk::ScopedAStatus enumerateEnrollments(int32_t cookie) override;
+ ndk::ScopedAStatus enumerateEnrollments() override;
- ndk::ScopedAStatus removeEnrollments(int32_t cookie,
- const std::vector<int32_t>& enrollmentIds) override;
+ ndk::ScopedAStatus removeEnrollments(const std::vector<int32_t>& enrollmentIds) override;
- ndk::ScopedAStatus getAuthenticatorId(int32_t cookie) override;
+ ndk::ScopedAStatus getAuthenticatorId() override;
- ndk::ScopedAStatus invalidateAuthenticatorId(int32_t cookie) override;
+ ndk::ScopedAStatus invalidateAuthenticatorId() override;
- ndk::ScopedAStatus resetLockout(int32_t cookie,
- const keymaster::HardwareAuthToken& hat) override;
+ ndk::ScopedAStatus resetLockout(const keymaster::HardwareAuthToken& hat) override;
- ndk::ScopedAStatus close(int32_t cookie) override;
+ ndk::ScopedAStatus close() override;
ndk::ScopedAStatus onPointerDown(int32_t pointerId, int32_t x, int32_t y, float minor,
float major) override;
@@ -76,11 +89,11 @@
// Crashes the HAL if the provided state doesn't match the previously scheduled state.
// Otherwise, transitions into the provided state, clears the scheduled state, and notifies
// the client about the transition by calling ISessionCallback#onStateChanged.
- void enterStateOrCrash(int cookie, SessionState state);
+ void enterStateOrCrash(SessionState state);
// Sets the current state to SessionState::IDLING and notifies the client about the transition
// by calling ISessionCallback#onStateChanged.
- void enterIdling(int cookie);
+ void enterIdling();
// The sensor and user IDs for which this session was created.
int32_t mSensorId;
diff --git a/biometrics/fingerprint/aidl/vts/VtsHalBiometricsFingerprintTargetTest.cpp b/biometrics/fingerprint/aidl/vts/VtsHalBiometricsFingerprintTargetTest.cpp
index 885f703..f1cfb17 100644
--- a/biometrics/fingerprint/aidl/vts/VtsHalBiometricsFingerprintTargetTest.cpp
+++ b/biometrics/fingerprint/aidl/vts/VtsHalBiometricsFingerprintTargetTest.cpp
@@ -22,46 +22,20 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
+#include <chrono>
#include <future>
namespace aidl::android::hardware::biometrics::fingerprint {
namespace {
+using namespace std::literals::chrono_literals;
+
constexpr int kSensorId = 0;
constexpr int kUserId = 0;
-constexpr auto kCallbackTimeout = std::chrono::seconds(1);
-
-enum class MethodName {
- kOnStateChanged,
-};
-
-struct Invocation {
- MethodName methodName;
- int32_t cookie;
- SessionState state;
-};
class SessionCallback : public BnSessionCallback {
public:
- explicit SessionCallback() : mIsPromiseValid(false) {}
-
- void setPromise(std::promise<std::vector<Invocation>>&& promise) {
- mPromise = std::move(promise);
- mIsPromiseValid = true;
- }
-
- ndk::ScopedAStatus onStateChanged(int32_t cookie, SessionState state) override {
- Invocation invocation = {};
- invocation.methodName = MethodName::kOnStateChanged;
- invocation.cookie = cookie;
- invocation.state = state;
- mInvocations.push_back(invocation);
- if (state == SessionState::IDLING) {
- assert(mIsPromiseValid);
- mPromise.set_value(mInvocations);
- }
- return ndk::ScopedAStatus::ok();
- }
+ explicit SessionCallback(std::promise<void>&& promise) : mPromise(std::move(promise)) {}
ndk::ScopedAStatus onChallengeGenerated(int64_t /*challenge*/) override {
return ndk::ScopedAStatus::ok();
@@ -119,12 +93,13 @@
return ndk::ScopedAStatus::ok();
}
- ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); }
+ ndk::ScopedAStatus onSessionClosed() override {
+ mPromise.set_value();
+ return ndk::ScopedAStatus::ok();
+ }
private:
- bool mIsPromiseValid;
- std::vector<Invocation> mInvocations;
- std::promise<std::vector<Invocation>> mPromise;
+ std::promise<void> mPromise;
};
class Fingerprint : public testing::TestWithParam<std::string> {
@@ -139,33 +114,26 @@
};
TEST_P(Fingerprint, AuthenticateTest) {
- // Prepare the callback
- std::promise<std::vector<Invocation>> promise;
+ auto promise = std::promise<void>{};
auto future = promise.get_future();
- std::shared_ptr<SessionCallback> cb = ndk::SharedRefBase::make<SessionCallback>();
- cb->setPromise(std::move(promise));
+ // Prepare the callback.
+ auto cb = ndk::SharedRefBase::make<SessionCallback>(std::move(promise));
// Create a session
std::shared_ptr<ISession> session;
ASSERT_TRUE(mHal->createSession(kSensorId, kUserId, cb, &session).isOk());
// Call authenticate
- int32_t cookie = 123;
std::shared_ptr<common::ICancellationSignal> cancellationSignal;
- ASSERT_TRUE(session->authenticate(cookie, 0, &cancellationSignal).isOk());
+ ASSERT_TRUE(session->authenticate(-1 /* operationId */, &cancellationSignal).isOk());
// Get the results
- ASSERT_TRUE(future.wait_for(kCallbackTimeout) == std::future_status::ready);
- std::vector<Invocation> invocations = future.get();
+ // TODO(b/166799066): test authenticate.
// Close the session
- ASSERT_TRUE(session->close(0).isOk());
-
- ASSERT_FALSE(invocations.empty());
- EXPECT_EQ(invocations.front().methodName, MethodName::kOnStateChanged);
- EXPECT_EQ(invocations.front().state, SessionState::AUTHENTICATING);
- EXPECT_EQ(invocations.back().methodName, MethodName::kOnStateChanged);
- EXPECT_EQ(invocations.back().state, SessionState::IDLING);
+ ASSERT_TRUE(session->close().isOk());
+ auto status = future.wait_for(1s);
+ ASSERT_EQ(status, std::future_status::ready);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Fingerprint);
diff --git a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
index 76777dc..8ea1ddd 100644
--- a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
+++ b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
@@ -179,7 +179,7 @@
bluetooth_cb->SetWaitTimeout(kCallbackNameScoEventReceived,
WAIT_FOR_SCO_DATA_TIMEOUT);
- EXPECT_TRUE(
+ ASSERT_TRUE(
bluetooth_cb->WaitForCallback(kCallbackNameInitializationComplete)
.no_timeout);
@@ -289,7 +289,7 @@
void BluetoothHidlTest::handle_no_ops() {
while (event_queue.size() > 0) {
hidl_vec<uint8_t> event = event_queue.front();
- EXPECT_GE(event.size(),
+ ASSERT_GE(event.size(),
static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
bool event_is_no_op =
(event[EVENT_CODE_BYTE] == EVENT_COMMAND_COMPLETE) &&
@@ -327,7 +327,7 @@
bluetooth_cb->WaitForCallback(kCallbackNameHciEventReceived).no_timeout;
EXPECT_TRUE(no_timeout || !timeout_is_error);
if (no_timeout && timeout_is_error) {
- EXPECT_LT(static_cast<size_t>(0), event_queue.size());
+ ASSERT_LT(static_cast<size_t>(0), event_queue.size());
}
if (event_queue.size() == 0) {
// WaitForCallback timed out.
@@ -343,12 +343,12 @@
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_GT(event.size(),
+ ASSERT_GT(event.size(),
static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
- EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+ ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
}
// Send the command to read the controller's buffer sizes.
@@ -362,10 +362,10 @@
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+ ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
max_acl_data_packet_length =
event[EVENT_COMMAND_COMPLETE_STATUS_BYTE + 1] +
@@ -415,10 +415,10 @@
size_t compare_length =
(cmd.size() > static_cast<size_t>(0xff) ? static_cast<size_t>(0xff)
: cmd.size());
- EXPECT_GT(event.size(), compare_length + EVENT_FIRST_PAYLOAD_BYTE - 1);
+ ASSERT_GT(event.size(), compare_length + EVENT_FIRST_PAYLOAD_BYTE - 1);
- EXPECT_EQ(EVENT_LOOPBACK_COMMAND, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(compare_length, event[EVENT_LENGTH_BYTE]);
+ ASSERT_EQ(EVENT_LOOPBACK_COMMAND, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(compare_length, event[EVENT_LENGTH_BYTE]);
// Don't compare past the end of the event.
if (compare_length + EVENT_FIRST_PAYLOAD_BYTE > event.size()) {
@@ -455,12 +455,12 @@
bluetooth->sendScoData(sco_vector);
// Check the loopback of the SCO packet
- EXPECT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameScoEventReceived)
+ ASSERT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameScoEventReceived)
.no_timeout);
hidl_vec<uint8_t> sco_loopback = sco_queue.front();
sco_queue.pop();
- EXPECT_EQ(sco_packet.size(), sco_loopback.size());
+ ASSERT_EQ(sco_packet.size(), sco_loopback.size());
size_t successful_bytes = 0;
for (size_t i = 0; i < sco_packet.size(); i++) {
@@ -474,7 +474,7 @@
break;
}
}
- EXPECT_EQ(sco_packet.size(), successful_bytes + 1);
+ ASSERT_EQ(sco_packet.size(), successful_bytes + 1);
}
logger.setTotalBytes(num_packets * size * 2);
}
@@ -500,26 +500,15 @@
bluetooth->sendAclData(acl_vector);
// Check the loopback of the ACL packet
- EXPECT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameAclEventReceived)
+ ASSERT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameAclEventReceived)
.no_timeout);
hidl_vec<uint8_t> acl_loopback = acl_queue.front();
acl_queue.pop();
EXPECT_EQ(acl_packet.size(), acl_loopback.size());
- size_t successful_bytes = 0;
-
- for (size_t i = 0; i < acl_packet.size(); i++) {
- if (acl_packet[i] == acl_loopback[i]) {
- successful_bytes = i;
- } else {
- ALOGE("Miscompare at %d (expected %x, got %x)", static_cast<int>(i),
- acl_packet[i], acl_loopback[i]);
- ALOGE("At %d (expected %x, got %x)", static_cast<int>(i + 1),
- acl_packet[i + 1], acl_loopback[i + 1]);
- break;
- }
+ for (size_t i = 0; i < acl_packet.size() && i < acl_loopback.size(); i++) {
+ EXPECT_EQ(acl_packet[i], acl_loopback[i]) << " at byte number " << i;
}
- EXPECT_EQ(acl_packet.size(), successful_bytes + 1);
}
logger.setTotalBytes(num_packets * size * 2);
}
@@ -560,22 +549,22 @@
wait_for_event(false);
if (event_queue.size() == 0) {
// Fail if there was no event received or no connections completed.
- EXPECT_TRUE(command_complete_received);
- EXPECT_LT(0, connection_event_count);
+ ASSERT_TRUE(command_complete_received);
+ ASSERT_LT(0, connection_event_count);
return;
}
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_GT(event.size(),
+ ASSERT_GT(event.size(),
static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
if (event[EVENT_CODE_BYTE] == EVENT_CONNECTION_COMPLETE) {
- EXPECT_GT(event.size(),
+ ASSERT_GT(event.size(),
static_cast<size_t>(EVENT_CONNECTION_COMPLETE_TYPE));
- EXPECT_EQ(event[EVENT_LENGTH_BYTE],
+ ASSERT_EQ(event[EVENT_LENGTH_BYTE],
EVENT_CONNECTION_COMPLETE_PARAM_LENGTH);
uint8_t connection_type = event[EVENT_CONNECTION_COMPLETE_TYPE];
- EXPECT_TRUE(connection_type == EVENT_CONNECTION_COMPLETE_TYPE_SCO ||
+ ASSERT_TRUE(connection_type == EVENT_CONNECTION_COMPLETE_TYPE_SCO ||
connection_type == EVENT_CONNECTION_COMPLETE_TYPE_ACL);
// Save handles
@@ -590,10 +579,10 @@
event[EVENT_CONNECTION_COMPLETE_TYPE], handle);
connection_event_count++;
} else {
- EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+ ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
command_complete_received = true;
}
}
@@ -620,15 +609,15 @@
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_GT(event.size(), static_cast<size_t>(EVENT_LOCAL_LMP_VERSION_BYTE));
+ ASSERT_GT(event.size(), static_cast<size_t>(EVENT_LOCAL_LMP_VERSION_BYTE));
- EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+ ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
- EXPECT_LE(HCI_MINIMUM_HCI_VERSION, event[EVENT_LOCAL_HCI_VERSION_BYTE]);
- EXPECT_LE(HCI_MINIMUM_LMP_VERSION, event[EVENT_LOCAL_LMP_VERSION_BYTE]);
+ ASSERT_LE(HCI_MINIMUM_HCI_VERSION, event[EVENT_LOCAL_HCI_VERSION_BYTE]);
+ ASSERT_LE(HCI_MINIMUM_LMP_VERSION, event[EVENT_LOCAL_LMP_VERSION_BYTE]);
}
// Send an unknown HCI command and wait for the error message.
@@ -642,18 +631,18 @@
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_GT(event.size(),
+ ASSERT_GT(event.size(),
static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
if (event[EVENT_CODE_BYTE] == EVENT_COMMAND_COMPLETE) {
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
} else {
- EXPECT_EQ(EVENT_COMMAND_STATUS, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
+ ASSERT_EQ(EVENT_COMMAND_STATUS, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
event[EVENT_COMMAND_STATUS_STATUS_BYTE]);
}
}
@@ -678,7 +667,7 @@
// This should work, but breaks on some current platforms. Figure out how to
// grandfather older devices but test new ones.
if (0 && sco_connection_handles.size() > 0) {
- EXPECT_LT(0, max_sco_data_packet_length);
+ ASSERT_LT(0, max_sco_data_packet_length);
sendAndCheckSCO(1, max_sco_data_packet_length, sco_connection_handles[0]);
int sco_packets_sent = 1;
int completed_packets =
@@ -690,7 +679,7 @@
}
if (acl_connection_handles.size() > 0) {
- EXPECT_LT(0, max_acl_data_packet_length);
+ ASSERT_LT(0, max_acl_data_packet_length);
sendAndCheckACL(1, max_acl_data_packet_length, acl_connection_handles[0]);
int acl_packets_sent = 1;
int completed_packets =
@@ -715,7 +704,7 @@
// This should work, but breaks on some current platforms. Figure out how to
// grandfather older devices but test new ones.
if (0 && sco_connection_handles.size() > 0) {
- EXPECT_LT(0, max_sco_data_packet_length);
+ ASSERT_LT(0, max_sco_data_packet_length);
sendAndCheckSCO(NUM_SCO_PACKETS_BANDWIDTH, max_sco_data_packet_length,
sco_connection_handles[0]);
int sco_packets_sent = NUM_SCO_PACKETS_BANDWIDTH;
@@ -728,7 +717,7 @@
}
if (acl_connection_handles.size() > 0) {
- EXPECT_LT(0, max_acl_data_packet_length);
+ ASSERT_LT(0, max_acl_data_packet_length);
sendAndCheckACL(NUM_ACL_PACKETS_BANDWIDTH, max_acl_data_packet_length,
acl_connection_handles[0]);
int acl_packets_sent = NUM_ACL_PACKETS_BANDWIDTH;
diff --git a/camera/device/3.7/Android.bp b/camera/device/3.7/Android.bp
index 163f781..15c1b5c 100644
--- a/camera/device/3.7/Android.bp
+++ b/camera/device/3.7/Android.bp
@@ -24,6 +24,11 @@
"android.hardware.camera.device@3.4",
"android.hardware.camera.device@3.5",
"android.hardware.camera.device@3.6",
+ "android.hardware.camera.metadata@3.2",
+ "android.hardware.camera.metadata@3.3",
+ "android.hardware.camera.metadata@3.4",
+ "android.hardware.camera.metadata@3.5",
+ "android.hardware.camera.metadata@3.6",
"android.hardware.graphics.common@1.0",
"android.hidl.base@1.0",
],
diff --git a/camera/device/3.7/types.hal b/camera/device/3.7/types.hal
index 9450c2f..6910e65 100644
--- a/camera/device/3.7/types.hal
+++ b/camera/device/3.7/types.hal
@@ -21,6 +21,8 @@
import @3.4::CaptureRequest;
import @3.4::Stream;
+import android.hardware.camera.metadata@3.6::CameraMetadataEnumAndroidSensorPixelMode;
+
/**
* Stream:
*
@@ -57,6 +59,14 @@
* usage flag.
*/
int32_t groupId;
+
+ /**
+ * The sensor pixel modes used by this stream. This can assist the camera
+ * HAL in decision making about stream combination support.
+ * If this is empty, the HAL must assume that this stream will only be used
+ * with ANDROID_SENSOR_PIXEL_MODE set to ANDROID_SENSOR_PIXEL_MODE_DEFAULT.
+ */
+ vec<CameraMetadataEnumAndroidSensorPixelMode> sensorPixelModesUsed;
};
/**
diff --git a/camera/metadata/3.6/types.hal b/camera/metadata/3.6/types.hal
index 3472ae9..709cfb3 100644
--- a/camera/metadata/3.6/types.hal
+++ b/camera/metadata/3.6/types.hal
@@ -36,6 +36,43 @@
* '/system/media/camera/docs/docs.html' in the corresponding Android source tree.</p>
*/
enum CameraMetadataTag : @3.5::CameraMetadataTag {
+ /** android.control.availableHighSpeedVideoConfigurationsMaximumResolution [static, int32[], hidden]
+ *
+ * <p>List of available high speed video size, fps range and max batch size configurations
+ * supported by the camera device, in the format of
+ * (width, height, fps_min, fps_max, batch_size_max),
+ * when ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS_MAXIMUM_RESOLUTION = android.hardware.camera.metadata@3.5::CameraMetadataTag:ANDROID_CONTROL_END_3_5,
+
+ ANDROID_CONTROL_END_3_6,
+
+ /** android.lens.distortionMaximumResolution [static, float[], public]
+ *
+ * <p>The correction coefficients to correct for this camera device's
+ * radial and tangential lens distortion for a
+ * CaptureRequest with ANDROID_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_LENS_DISTORTION_MAXIMUM_RESOLUTION = android.hardware.camera.metadata@3.3::CameraMetadataTag:ANDROID_LENS_END_3_3,
+
+ /** android.lens.intrinsicCalibrationMaximumResolution [static, float[], public]
+ *
+ * <p>The parameters for this camera device's intrinsic
+ * calibration when ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION,
+
+ ANDROID_LENS_END_3_6,
+
/** android.scaler.defaultSecureImageSize [static, int32[], public]
*
* <p>Default YUV/PRIVATE size to use for requesting secure image buffers.</p>
@@ -50,14 +87,237 @@
*/
ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS,
+ /** android.scaler.availableStreamConfigurationsMaximumResolution [static, enum[], ndk_public]
+ *
+ * <p>The available stream configurations that this
+ * camera device supports (i.e. format, width, height, output/input stream) for a
+ * CaptureRequest with ANDROID_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+
+ /** android.scaler.availableMinFrameDurationsMaximumResolution [static, int64[], ndk_public]
+ *
+ * <p>This lists the minimum frame duration for each
+ * format/size combination when the camera device is sent a CaptureRequest with
+ * ANDROID_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
+
+ /** android.scaler.availableStallDurationsMaximumResolution [static, int64[], ndk_public]
+ *
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination when CaptureRequests are submitted with
+ * ANDROID_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a></p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+
+ /** android.scaler.availableInputOutputFormatsMapMaximumResolution [static, int32, hidden]
+ *
+ * <p>The mapping of image formats that are supported by this
+ * camera device for input streams, to their corresponding output formats, when
+ * ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION,
+
ANDROID_SCALER_END_3_6,
+ /** android.sensor.opaqueRawSizeMaximumResolution [static, int32[], system]
+ *
+ * <p>Size in bytes for all the listed opaque RAW buffer sizes when
+ * ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_SENSOR_END,
+
+ /** android.sensor.pixelMode [dynamic, enum, public]
+ *
+ * <p>Switches sensor pixel mode between maximum resolution mode and default mode.</p>
+ */
+ ANDROID_SENSOR_PIXEL_MODE,
+
+ /** android.sensor.rawBinningFactorUsed [dynamic, enum, public]
+ *
+ * <p>Whether <code>RAW</code> images requested have their bayer pattern as described by
+ * ANDROID_SENSOR_INFO_BINNING_FACTOR.</p>
+ *
+ * @see ANDROID_SENSOR_INFO_BINNING_FACTOR
+ */
+ ANDROID_SENSOR_RAW_BINNING_FACTOR_USED,
+
+ ANDROID_SENSOR_END_3_6,
+
+ /** android.sensor.info.activeArraySizeMaximumResolution [static, int32[], public]
+ *
+ * <p>The area of the image sensor which corresponds to active pixels after any geometric
+ * distortion correction has been applied, when the sensor runs in maximum resolution mode.</p>
+ */
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_SENSOR_INFO_END,
+
+ /** android.sensor.info.pixelArraySizeMaximumResolution [static, int32[], public]
+ *
+ * <p>Dimensions of the full pixel array, possibly
+ * including black calibration pixels, when the sensor runs in maximum resolution mode.
+ * Analogous to ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, when ANDROID_SENSOR_PIXEL_MODE is
+ * set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+
+ /** android.sensor.info.preCorrectionActiveArraySizeMaximumResolution [static, int32[], public]
+ *
+ * <p>The area of the image sensor which corresponds to active pixels prior to the
+ * application of any geometric distortion correction, when the sensor runs in maximum
+ * resolution mode. This key must be used for crop / metering regions, only when
+ * ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+
+ /** android.sensor.info.binningFactor [static, int32[], public]
+ *
+ * <p>Dimensions of the group of pixels which are under the same color filter.
+ * This specifies the width and height (pair of integers) of the group of pixels which fall
+ * under the same color filter for ULTRA_HIGH_RESOLUTION sensors.</p>
+ */
+ ANDROID_SENSOR_INFO_BINNING_FACTOR,
+
+ ANDROID_SENSOR_INFO_END_3_6,
+
+ /** android.depth.availableDepthStreamConfigurationsMaximumResolution [static, enum[], ndk_public]
+ *
+ * <p>The available depth dataspace stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream) when a CaptureRequest is submitted with
+ * ANDROID_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION = android.hardware.camera.metadata@3.4::CameraMetadataTag:ANDROID_DEPTH_END_3_4,
+
+ /** android.depth.availableDepthMinFrameDurationsMaximumResolution [static, int64[], ndk_public]
+ *
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for depth output formats when a CaptureRequest is submitted with
+ * ANDROID_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
+
+ /** android.depth.availableDepthStallDurationsMaximumResolution [static, int64[], ndk_public]
+ *
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for depth streams for CaptureRequests where
+ * ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+
+ /** android.depth.availableDynamicDepthStreamConfigurationsMaximumResolution [static, enum[], ndk_public]
+ *
+ * <p>The available dynamic depth dataspace stream
+ * configurations that this camera device supports (i.e. format, width, height,
+ * output/input stream) for CaptureRequests where ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+
+ /** android.depth.availableDynamicDepthMinFrameDurationsMaximumResolution [static, int64[], ndk_public]
+ *
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for dynamic depth output streams for CaptureRequests where
+ * ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
+
+ /** android.depth.availableDynamicDepthStallDurationsMaximumResolution [static, int64[], ndk_public]
+ *
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for dynamic depth streams for CaptureRequests where
+ * ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+
+ ANDROID_DEPTH_END_3_6,
+
+ /** android.heic.availableHeicStreamConfigurationsMaximumResolution [static, enum[], ndk_public]
+ *
+ * <p>The available HEIC (ISO/IEC 23008-12) stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ */
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION = android.hardware.camera.metadata@3.4::CameraMetadataTag:ANDROID_HEIC_END_3_4,
+
+ /** android.heic.availableHeicMinFrameDurationsMaximumResolution [static, int64[], ndk_public]
+ *
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC output formats for CaptureRequests where
+ * ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
+
+ /** android.heic.availableHeicStallDurationsMaximumResolution [static, int64[], ndk_public]
+ *
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC streams for CaptureRequests where
+ * ANDROID_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+ ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+
+ ANDROID_HEIC_END_3_6,
+
};
/*
* Enumeration definitions for the various entries that need them
*/
+/** android.request.availableCapabilities enumeration values added since v3.5
+ * @see ANDROID_REQUEST_AVAILABLE_CAPABILITIES
+ */
+enum CameraMetadataEnumAndroidRequestAvailableCapabilities :
+ @3.5::CameraMetadataEnumAndroidRequestAvailableCapabilities {
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR,
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING,
+};
+
/** android.scaler.physicalCameraMultiResolutionStreamConfigurations enumeration values
* @see ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS
*/
@@ -65,3 +325,51 @@
ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS_OUTPUT,
ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS_INPUT,
};
+
+/** android.scaler.availableStreamConfigurationsMaximumResolution enumeration values
+ * @see ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+ */
+enum CameraMetadataEnumAndroidScalerAvailableStreamConfigurationsMaximumResolution : uint32_t {
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT,
+};
+
+/** android.sensor.pixelMode enumeration values
+ * @see ANDROID_SENSOR_PIXEL_MODE
+ */
+enum CameraMetadataEnumAndroidSensorPixelMode : uint32_t {
+ ANDROID_SENSOR_PIXEL_MODE_DEFAULT,
+ ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION,
+};
+
+/** android.sensor.rawBinningFactorUsed enumeration values
+ * @see ANDROID_SENSOR_RAW_BINNING_FACTOR_USED
+ */
+enum CameraMetadataEnumAndroidSensorRawBinningFactorUsed : uint32_t {
+ ANDROID_SENSOR_RAW_BINNING_FACTOR_USED_TRUE,
+ ANDROID_SENSOR_RAW_BINNING_FACTOR_USED_FALSE,
+};
+
+/** android.depth.availableDepthStreamConfigurationsMaximumResolution enumeration values
+ * @see ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+ */
+enum CameraMetadataEnumAndroidDepthAvailableDepthStreamConfigurationsMaximumResolution : uint32_t {
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT,
+};
+
+/** android.depth.availableDynamicDepthStreamConfigurationsMaximumResolution enumeration values
+ * @see ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+ */
+enum CameraMetadataEnumAndroidDepthAvailableDynamicDepthStreamConfigurationsMaximumResolution : uint32_t {
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT,
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT,
+};
+
+/** android.heic.availableHeicStreamConfigurationsMaximumResolution enumeration values
+ * @see ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+ */
+enum CameraMetadataEnumAndroidHeicAvailableHeicStreamConfigurationsMaximumResolution : uint32_t {
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT,
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT,
+};
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index b2fd402..ed3b1fa 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -125,6 +125,7 @@
using ::android::hardware::camera::metadata::V3_4::
CameraMetadataEnumAndroidSensorInfoColorFilterArrangement;
using ::android::hardware::camera::metadata::V3_4::CameraMetadataTag;
+using ::android::hardware::camera::metadata::V3_6::CameraMetadataEnumAndroidSensorPixelMode;
using ::android::hardware::camera::provider::V2_4::ICameraProvider;
using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
using ::android::hardware::camera::provider::V2_6::CameraIdAndStreamCombination;
@@ -767,6 +768,8 @@
sp<device::V3_7::ICameraDeviceSession> *session3_7 /*out*/);
void castDevice(const sp<device::V3_2::ICameraDevice> &device, int32_t deviceVersion,
sp<device::V3_5::ICameraDevice> *device3_5/*out*/);
+ void castDevice3_7(const sp<device::V3_2::ICameraDevice>& device, int32_t deviceVersion,
+ sp<device::V3_7::ICameraDevice>* device3_7 /*out*/);
void createStreamConfiguration(const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
StreamConfigurationMode configMode,
::android::hardware::camera::device::V3_2::StreamConfiguration *config3_2,
@@ -785,6 +788,16 @@
sp<DeviceCb> *outCb /*out*/,
uint32_t *jpegBufferSize /*out*/,
bool *useHalBufManager /*out*/);
+ void configureStreams3_7(const std::string& name, int32_t deviceVersion,
+ sp<ICameraProvider> provider, PixelFormat format,
+ sp<device::V3_7::ICameraDeviceSession>* session3_7 /*out*/,
+ V3_2::Stream* previewStream /*out*/,
+ device::V3_6::HalStreamConfiguration* halStreamConfig /*out*/,
+ bool* supportsPartialResults /*out*/,
+ uint32_t* partialResultCount /*out*/, bool* useHalBufManager /*out*/,
+ sp<DeviceCb>* outCb /*out*/, uint32_t streamConfigCounter,
+ bool maxResolution);
+
void configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
sp<ICameraProvider> provider,
const AvailableStream *previewThreshold,
@@ -846,6 +859,10 @@
hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
uint32_t streamConfigCounter = 0);
+ void verifyBuffersReturned(sp<device::V3_7::ICameraDeviceSession> session,
+ hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
+ uint32_t streamConfigCounter = 0);
+
void verifySessionReconfigurationQuery(sp<device::V3_5::ICameraDeviceSession> session3_5,
camera_metadata* oldSessionParams, camera_metadata* newSessionParams);
@@ -853,12 +870,15 @@
static bool isDepthOnly(const camera_metadata_t* staticMeta);
- static Status getAvailableOutputStreams(const camera_metadata_t *staticMeta,
- std::vector<AvailableStream> &outputStreams,
- const AvailableStream *threshold = nullptr);
+ static bool isUltraHighResolution(const camera_metadata_t* staticMeta);
+
+ static Status getAvailableOutputStreams(const camera_metadata_t* staticMeta,
+ std::vector<AvailableStream>& outputStreams,
+ const AvailableStream* threshold = nullptr,
+ bool maxResolution = false);
static Status getMaxOutputSizeForFormat(const camera_metadata_t* staticMeta, PixelFormat format,
- Size* size);
+ Size* size, bool maxResolution = false);
static Status getMandatoryConcurrentStreams(const camera_metadata_t* staticMeta,
std::vector<AvailableStream>* outputStreams);
@@ -4841,6 +4861,184 @@
}
}
+// Generate and verify an ultra high resolution capture request
+TEST_P(CameraHidlTest, processUltraHighResolutionRequest) {
+ hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+ uint64_t bufferId = 1;
+ uint32_t frameNumber = 1;
+ ::android::hardware::hidl_vec<uint8_t> settings;
+
+ for (const auto& name : cameraDeviceNames) {
+ int deviceVersion = getCameraDeviceVersion(name, mProviderType);
+ if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_7) {
+ continue;
+ }
+ std::string version, deviceId;
+ ASSERT_TRUE(::matchDeviceName(name, mProviderType, &version, &deviceId));
+ camera_metadata_t* staticMeta;
+ Return<void> ret;
+ sp<ICameraDeviceSession> session;
+ openEmptyDeviceSession(name, mProvider, &session, &staticMeta);
+ if (!isUltraHighResolution(staticMeta)) {
+ free_camera_metadata(staticMeta);
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+ continue;
+ }
+ android::hardware::camera::common::V1_0::helper::CameraMetadata defaultSettings;
+ ret = session->constructDefaultRequestSettings(
+ RequestTemplate::STILL_CAPTURE,
+ [&defaultSettings](auto status, const auto& req) mutable {
+ ASSERT_EQ(Status::OK, status);
+
+ const camera_metadata_t* metadata =
+ reinterpret_cast<const camera_metadata_t*>(req.data());
+ size_t expectedSize = req.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);
+ defaultSettings = metadata;
+ });
+ ASSERT_TRUE(ret.isOk());
+ uint8_t sensorPixelMode =
+ static_cast<uint8_t>(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
+ ASSERT_EQ(::android::OK,
+ defaultSettings.update(ANDROID_SENSOR_PIXEL_MODE, &sensorPixelMode, 1));
+
+ const camera_metadata_t* settingsBuffer = defaultSettings.getAndLock();
+ settings.setToExternal(
+ reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(settingsBuffer)),
+ get_camera_metadata_size(settingsBuffer));
+
+ free_camera_metadata(staticMeta);
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+ V3_6::HalStreamConfiguration halStreamConfig;
+ bool supportsPartialResults = false;
+ bool useHalBufManager = false;
+ uint32_t partialResultCount = 0;
+ V3_2::Stream previewStream;
+ sp<device::V3_7::ICameraDeviceSession> session3_7;
+ sp<DeviceCb> cb;
+ std::list<PixelFormat> pixelFormats = {PixelFormat::YCBCR_420_888, PixelFormat::RAW16};
+ for (PixelFormat format : pixelFormats) {
+ configureStreams3_7(name, deviceVersion, mProvider, format, &session3_7, &previewStream,
+ &halStreamConfig, &supportsPartialResults, &partialResultCount,
+ &useHalBufManager, &cb, 0, /*maxResolution*/ true);
+ ASSERT_NE(session3_7, nullptr);
+
+ std::shared_ptr<ResultMetadataQueue> resultQueue;
+ auto resultQueueRet = session3_7->getCaptureResultMetadataQueue(
+ [&resultQueue](const auto& descriptor) {
+ resultQueue = std::make_shared<ResultMetadataQueue>(descriptor);
+ if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) {
+ ALOGE("%s: HAL returns empty result metadata fmq,"
+ " not use it",
+ __func__);
+ resultQueue = nullptr;
+ // Don't use the queue onwards.
+ }
+ });
+ ASSERT_TRUE(resultQueueRet.isOk());
+
+ std::vector<hidl_handle> graphicBuffers;
+ graphicBuffers.reserve(halStreamConfig.streams.size());
+ ::android::hardware::hidl_vec<StreamBuffer> outputBuffers;
+ outputBuffers.resize(halStreamConfig.streams.size());
+ InFlightRequest inflightReq = {static_cast<ssize_t>(halStreamConfig.streams.size()),
+ false,
+ supportsPartialResults,
+ partialResultCount,
+ std::unordered_set<std::string>(),
+ resultQueue};
+
+ size_t k = 0;
+ for (const auto& halStream : halStreamConfig.streams) {
+ hidl_handle buffer_handle;
+ if (useHalBufManager) {
+ outputBuffers[k] = {halStream.v3_4.v3_3.v3_2.id,
+ 0,
+ buffer_handle,
+ BufferStatus::OK,
+ nullptr,
+ nullptr};
+ } else {
+ allocateGraphicBuffer(
+ previewStream.width, previewStream.height,
+ android_convertGralloc1To0Usage(halStream.v3_4.v3_3.v3_2.producerUsage,
+ halStream.v3_4.v3_3.v3_2.consumerUsage),
+ halStream.v3_4.v3_3.v3_2.overrideFormat, &buffer_handle);
+ graphicBuffers.push_back(buffer_handle);
+ outputBuffers[k] = {halStream.v3_4.v3_3.v3_2.id,
+ bufferId,
+ buffer_handle,
+ BufferStatus::OK,
+ nullptr,
+ nullptr};
+ bufferId++;
+ }
+ k++;
+ }
+
+ StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
+ V3_4::CaptureRequest request3_4;
+ request3_4.v3_2.frameNumber = frameNumber;
+ request3_4.v3_2.fmqSettingsSize = 0;
+ request3_4.v3_2.settings = settings;
+ request3_4.v3_2.inputBuffer = emptyInputBuffer;
+ request3_4.v3_2.outputBuffers = outputBuffers;
+ V3_7::CaptureRequest request3_7;
+ request3_7.v3_4 = request3_4;
+ request3_7.inputWidth = 0;
+ request3_7.inputHeight = 0;
+
+ {
+ std::unique_lock<std::mutex> l(mLock);
+ mInflightMap.clear();
+ mInflightMap.add(frameNumber, &inflightReq);
+ }
+
+ Status stat = Status::INTERNAL_ERROR;
+ uint32_t numRequestProcessed = 0;
+ hidl_vec<BufferCache> cachesToRemove;
+ Return<void> returnStatus = session3_7->processCaptureRequest_3_7(
+ {request3_7}, cachesToRemove,
+ [&stat, &numRequestProcessed](auto s, uint32_t n) {
+ stat = s;
+ numRequestProcessed = n;
+ });
+ ASSERT_TRUE(returnStatus.isOk());
+ ASSERT_EQ(Status::OK, stat);
+ ASSERT_EQ(numRequestProcessed, 1u);
+
+ {
+ std::unique_lock<std::mutex> l(mLock);
+ while (!inflightReq.errorCodeValid &&
+ ((0 < inflightReq.numBuffersLeft) || (!inflightReq.haveResultMetadata))) {
+ auto timeout = std::chrono::system_clock::now() +
+ std::chrono::seconds(kStreamBufferTimeoutSec);
+ ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
+ }
+
+ ASSERT_FALSE(inflightReq.errorCodeValid);
+ ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
+ }
+ if (useHalBufManager) {
+ hidl_vec<int32_t> streamIds(halStreamConfig.streams.size());
+ for (size_t i = 0; i < streamIds.size(); i++) {
+ streamIds[i] = halStreamConfig.streams[i].v3_4.v3_3.v3_2.id;
+ }
+ verifyBuffersReturned(session3_7, streamIds, cb);
+ }
+
+ ret = session3_7->close();
+ ASSERT_TRUE(ret.isOk());
+ }
+ }
+}
+
// Generate and verify a burst containing alternating sensor sensitivity values
TEST_P(CameraHidlTest, processCaptureRequestBurstISO) {
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
@@ -5537,21 +5735,26 @@
// Retrieve all valid output stream resolutions from the camera
// static characteristics.
-Status CameraHidlTest::getAvailableOutputStreams(const camera_metadata_t *staticMeta,
- std::vector<AvailableStream> &outputStreams,
- const AvailableStream *threshold) {
+Status CameraHidlTest::getAvailableOutputStreams(const camera_metadata_t* staticMeta,
+ std::vector<AvailableStream>& outputStreams,
+ const AvailableStream* threshold,
+ bool maxResolution) {
AvailableStream depthPreviewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
static_cast<int32_t>(PixelFormat::Y16)};
if (nullptr == staticMeta) {
return Status::ILLEGAL_ARGUMENT;
}
+ int scalerTag = maxResolution
+ ? ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+ : ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+ int depthTag = maxResolution
+ ? ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+ : ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS;
camera_metadata_ro_entry scalarEntry;
camera_metadata_ro_entry depthEntry;
- int foundScalar = find_camera_metadata_ro_entry(staticMeta,
- ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &scalarEntry);
- int foundDepth = find_camera_metadata_ro_entry(staticMeta,
- ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, &depthEntry);
+ int foundScalar = find_camera_metadata_ro_entry(staticMeta, scalerTag, &scalarEntry);
+ int foundDepth = find_camera_metadata_ro_entry(staticMeta, depthTag, &depthEntry);
if ((0 != foundScalar || (0 != (scalarEntry.count % 4))) &&
(0 != foundDepth || (0 != (depthEntry.count % 4)))) {
return Status::ILLEGAL_ARGUMENT;
@@ -5619,9 +5822,12 @@
}
Status CameraHidlTest::getMaxOutputSizeForFormat(const camera_metadata_t* staticMeta,
- PixelFormat format, Size* size) {
+ PixelFormat format, Size* size,
+ bool maxResolution) {
std::vector<AvailableStream> outputStreams;
- if (size == nullptr || getAvailableOutputStreams(staticMeta, outputStreams) != Status::OK) {
+ if (size == nullptr ||
+ getAvailableOutputStreams(staticMeta, outputStreams,
+ /*threshold*/ nullptr, maxResolution) != Status::OK) {
return Status::ILLEGAL_ARGUMENT;
}
Size maxSize;
@@ -6065,6 +6271,148 @@
*config3_2 = {streams3_2, configMode};
}
+// Configure streams
+void CameraHidlTest::configureStreams3_7(
+ const std::string& name, int32_t deviceVersion, sp<ICameraProvider> provider,
+ PixelFormat format, sp<device::V3_7::ICameraDeviceSession>* session3_7 /*out*/,
+ V3_2::Stream* previewStream /*out*/,
+ device::V3_6::HalStreamConfiguration* halStreamConfig /*out*/,
+ bool* supportsPartialResults /*out*/, uint32_t* partialResultCount /*out*/,
+ bool* useHalBufManager /*out*/, sp<DeviceCb>* outCb /*out*/, uint32_t streamConfigCounter,
+ bool maxResolution) {
+ ASSERT_NE(nullptr, session3_7);
+ ASSERT_NE(nullptr, halStreamConfig);
+ ASSERT_NE(nullptr, previewStream);
+ ASSERT_NE(nullptr, supportsPartialResults);
+ ASSERT_NE(nullptr, partialResultCount);
+ ASSERT_NE(nullptr, useHalBufManager);
+ ASSERT_NE(nullptr, outCb);
+
+ std::vector<AvailableStream> outputStreams;
+ ::android::sp<ICameraDevice> device3_x;
+ ALOGI("configureStreams: Testing camera device %s", name.c_str());
+ Return<void> ret;
+ ret = provider->getCameraDeviceInterface_V3_x(name, [&](auto status, const auto& device) {
+ ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+ ASSERT_EQ(Status::OK, status);
+ ASSERT_NE(device, nullptr);
+ device3_x = device;
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ camera_metadata_t* staticMeta;
+ ret = device3_x->getCameraCharacteristics([&](Status s, CameraMetadata metadata) {
+ ASSERT_EQ(Status::OK, s);
+ staticMeta =
+ clone_camera_metadata(reinterpret_cast<const camera_metadata_t*>(metadata.data()));
+ ASSERT_NE(nullptr, staticMeta);
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ camera_metadata_ro_entry entry;
+ auto status =
+ find_camera_metadata_ro_entry(staticMeta, ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry);
+ if ((0 == status) && (entry.count > 0)) {
+ *partialResultCount = entry.data.i32[0];
+ *supportsPartialResults = (*partialResultCount > 1);
+ }
+
+ sp<DeviceCb> cb = new DeviceCb(this, deviceVersion, staticMeta);
+ sp<ICameraDeviceSession> session;
+ ret = device3_x->open(cb, [&session](auto status, const auto& newSession) {
+ ALOGI("device::open returns status:%d", (int)status);
+ ASSERT_EQ(Status::OK, status);
+ ASSERT_NE(newSession, nullptr);
+ session = newSession;
+ });
+ ASSERT_TRUE(ret.isOk());
+ *outCb = cb;
+
+ sp<device::V3_3::ICameraDeviceSession> session3_3;
+ sp<device::V3_4::ICameraDeviceSession> session3_4;
+ sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6,
+ session3_7);
+ ASSERT_NE(nullptr, (*session3_7).get());
+
+ *useHalBufManager = false;
+ status = find_camera_metadata_ro_entry(
+ staticMeta, ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
+ if ((0 == status) && (entry.count == 1)) {
+ *useHalBufManager = (entry.data.u8[0] ==
+ ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+ }
+
+ outputStreams.clear();
+ Size maxSize;
+ auto rc = getMaxOutputSizeForFormat(staticMeta, format, &maxSize, maxResolution);
+ ASSERT_EQ(Status::OK, rc);
+ free_camera_metadata(staticMeta);
+
+ ::android::hardware::hidl_vec<V3_7::Stream> streams3_7(1);
+ streams3_7[0].groupId = -1;
+ streams3_7[0].sensorPixelModesUsed = {
+ CameraMetadataEnumAndroidSensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION};
+ streams3_7[0].v3_4.bufferSize = 0;
+ streams3_7[0].v3_4.v3_2.id = 0;
+ streams3_7[0].v3_4.v3_2.streamType = StreamType::OUTPUT;
+ streams3_7[0].v3_4.v3_2.width = static_cast<uint32_t>(maxSize.width);
+ streams3_7[0].v3_4.v3_2.height = static_cast<uint32_t>(maxSize.height);
+ streams3_7[0].v3_4.v3_2.format = static_cast<PixelFormat>(format);
+ streams3_7[0].v3_4.v3_2.usage = GRALLOC1_CONSUMER_USAGE_CPU_READ;
+ streams3_7[0].v3_4.v3_2.dataSpace = 0;
+ streams3_7[0].v3_4.v3_2.rotation = StreamRotation::ROTATION_0;
+
+ ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
+ config3_7.streams = streams3_7;
+ config3_7.operationMode = StreamConfigurationMode::NORMAL_MODE;
+ config3_7.streamConfigCounter = streamConfigCounter;
+ config3_7.multiResolutionInputImage = false;
+ RequestTemplate reqTemplate = RequestTemplate::STILL_CAPTURE;
+ ret = (*session3_7)
+ ->constructDefaultRequestSettings(reqTemplate,
+ [&config3_7](auto status, const auto& req) {
+ ASSERT_EQ(Status::OK, status);
+ config3_7.sessionParams = req;
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ ASSERT_TRUE(deviceVersion >= CAMERA_DEVICE_API_VERSION_3_7);
+ sp<device::V3_7::ICameraDevice> cameraDevice3_7 = nullptr;
+ castDevice3_7(device3_x, deviceVersion, &cameraDevice3_7);
+ ASSERT_NE(cameraDevice3_7, nullptr);
+ bool supported = false;
+ ret = cameraDevice3_7->isStreamCombinationSupported_3_7(
+ config3_7, [&supported](Status s, bool combStatus) {
+ ASSERT_TRUE((Status::OK == s) || (Status::METHOD_NOT_SUPPORTED == s));
+ if (Status::OK == s) {
+ supported = combStatus;
+ }
+ });
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(supported, true);
+
+ if (*session3_7 != nullptr) {
+ ret = (*session3_7)
+ ->configureStreams_3_7(
+ config3_7,
+ [&](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+ ASSERT_EQ(Status::OK, s);
+ *halStreamConfig = halConfig;
+ if (*useHalBufManager) {
+ hidl_vec<V3_4::Stream> streams(1);
+ hidl_vec<V3_2::HalStream> halStreams(1);
+ streams[0] = streams3_7[0].v3_4;
+ halStreams[0] = halConfig.streams[0].v3_4.v3_3.v3_2;
+ cb->setCurrentStreamConfig(streams, halStreams);
+ }
+ });
+ }
+ *previewStream = streams3_7[0].v3_4.v3_2;
+ ASSERT_TRUE(ret.isOk());
+}
+
// Configure multiple preview streams using different physical ids.
void CameraHidlTest::configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
sp<ICameraProvider> provider,
@@ -6362,6 +6710,21 @@
ASSERT_TRUE(ret.isOk());
}
+bool CameraHidlTest::isUltraHighResolution(const camera_metadata_t* staticMeta) {
+ camera_metadata_ro_entry scalarEntry;
+ int rc = find_camera_metadata_ro_entry(staticMeta, ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ &scalarEntry);
+ if (rc == 0) {
+ for (uint32_t i = 0; i < scalarEntry.count; i++) {
+ if (scalarEntry.data.u8[i] ==
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool CameraHidlTest::isDepthOnly(const camera_metadata_t* staticMeta) {
camera_metadata_ro_entry scalarEntry;
camera_metadata_ro_entry depthEntry;
@@ -6590,6 +6953,17 @@
ASSERT_TRUE(ret.isOk());
}
+void CameraHidlTest::castDevice3_7(const sp<device::V3_2::ICameraDevice>& device,
+ int32_t deviceVersion,
+ sp<device::V3_7::ICameraDevice>* device3_7 /*out*/) {
+ ASSERT_NE(nullptr, device3_7);
+ if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_7) {
+ auto castResult = device::V3_7::ICameraDevice::castFrom(device);
+ ASSERT_TRUE(castResult.isOk());
+ *device3_7 = castResult;
+ }
+}
+
void CameraHidlTest::castDevice(const sp<device::V3_2::ICameraDevice> &device,
int32_t deviceVersion, sp<device::V3_5::ICameraDevice> *device3_5/*out*/) {
ASSERT_NE(nullptr, device3_5);
@@ -7303,6 +7677,13 @@
cb->waitForBuffersReturned();
}
+void CameraHidlTest::verifyBuffersReturned(sp<device::V3_7::ICameraDeviceSession> session3_7,
+ hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
+ uint32_t streamConfigCounter) {
+ session3_7->signalStreamFlush(streamIds, /*streamConfigCounter*/ streamConfigCounter);
+ cb->waitForBuffersReturned();
+}
+
void CameraHidlTest::verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& resultMetadata) {
std::unordered_set<std::string> physicalIds;
diff --git a/contexthub/1.2/types.hal b/contexthub/1.2/types.hal
index 5033ce8..75122bc 100644
--- a/contexthub/1.2/types.hal
+++ b/contexthub/1.2/types.hal
@@ -35,10 +35,12 @@
AIRPLANE_MODE,
/**
- * Indicates if the microphone access was turned off globally by the user,
- * in which case audio data cannot be used and propagated by CHRE.
+ * Indicates if the microphone access is available for CHRE. Microphone
+ * access is disabled if the user has turned off the microphone as a
+ * privacy setting, in which case audio data cannot be used and propagated
+ * by CHRE.
*/
- GLOBAL_MIC_DISABLE,
+ MICROPHONE,
};
struct ContextHubMsg {
diff --git a/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp b/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp
index 3510c23..9ee40ed 100644
--- a/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp
+++ b/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp
@@ -131,10 +131,10 @@
ASSERT_OK(registerCallback_1_2(nullptr));
}
-TEST_P(ContexthubHidlTest, TestOnGlobalMicDisableSettingChanged) {
+TEST_P(ContexthubHidlTest, TestOnMicrophoneSettingChanged) {
ASSERT_OK(registerCallback_1_2(new ContexthubCallbackV1_2()));
- hubApi->onSettingChanged_1_2(Setting::GLOBAL_MIC_DISABLE, SettingValue::DISABLED);
- hubApi->onSettingChanged_1_2(Setting::GLOBAL_MIC_DISABLE, SettingValue::ENABLED);
+ hubApi->onSettingChanged_1_2(Setting::MICROPHONE, SettingValue::DISABLED);
+ hubApi->onSettingChanged_1_2(Setting::MICROPHONE, SettingValue::ENABLED);
ASSERT_OK(registerCallback_1_2(nullptr));
}
diff --git a/drm/1.0/default/Android.bp b/drm/1.0/default/Android.bp
index af1c076..cbdab4f 100644
--- a/drm/1.0/default/Android.bp
+++ b/drm/1.0/default/Android.bp
@@ -32,6 +32,7 @@
"-Werror",
"-Wextra",
"-Wall",
+ "-Wthread-safety",
],
shared_libs: [
"liblog",
@@ -42,7 +43,7 @@
export_header_lib_headers: [
"libutils_headers",
],
- export_include_dirs : ["include"]
+ export_include_dirs: ["include"],
}
soong_config_module_type {
@@ -59,8 +60,8 @@
soong_config_variables: {
TARGET_ENABLE_MEDIADRM_64: {
compile_multilib: "both",
- }
- }
+ },
+ },
}
android_hardware_drm_1_0_multilib {
@@ -69,8 +70,8 @@
soong_config_variables: {
TARGET_ENABLE_MEDIADRM_64: {
compile_multilib: "first",
- }
- }
+ },
+ },
}
cc_defaults {
@@ -98,7 +99,7 @@
name: "android.hardware.drm@1.0-service",
defaults: [
"android.hardware.drm@1.0-multilib-exe",
- "android.hardware.drm@1.0-service-defaults"
+ "android.hardware.drm@1.0-service-defaults",
],
init_rc: ["android.hardware.drm@1.0-service.rc"],
srcs: ["service.cpp"],
@@ -110,7 +111,7 @@
name: "android.hardware.drm@1.0-service-lazy",
defaults: [
"android.hardware.drm@1.0-multilib-exe",
- "android.hardware.drm@1.0-service-defaults"
+ "android.hardware.drm@1.0-service-defaults",
],
overrides: ["android.hardware.drm@1.0-service"],
init_rc: ["android.hardware.drm@1.0-service-lazy.rc"],
diff --git a/drm/1.0/default/CryptoPlugin.cpp b/drm/1.0/default/CryptoPlugin.cpp
index e6d4e84..8dea7e9 100644
--- a/drm/1.0/default/CryptoPlugin.cpp
+++ b/drm/1.0/default/CryptoPlugin.cpp
@@ -53,6 +53,8 @@
uint32_t bufferId) {
sp<IMemory> hidlMemory = mapMemory(base);
+ std::lock_guard<std::mutex> shared_buffer_lock(mSharedBufferLock);
+
// allow mapMemory to return nullptr
mSharedBufferMap[bufferId] = hidlMemory;
return Void();
@@ -65,7 +67,7 @@
const SharedBuffer& source, uint64_t offset,
const DestinationBuffer& destination,
decrypt_cb _hidl_cb) {
-
+ std::unique_lock<std::mutex> shared_buffer_lock(mSharedBufferLock);
if (mSharedBufferMap.find(source.bufferId) == mSharedBufferMap.end()) {
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "source decrypt buffer base not set");
return Void();
@@ -79,7 +81,7 @@
}
}
- android::CryptoPlugin::Mode legacyMode;
+ android::CryptoPlugin::Mode legacyMode = android::CryptoPlugin::kMode_Unencrypted;
switch(mode) {
case Mode::UNENCRYPTED:
legacyMode = android::CryptoPlugin::kMode_Unencrypted;
@@ -146,7 +148,10 @@
return Void();
}
- if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
+ size_t totalSize = 0;
+ if (__builtin_add_overflow(destBuffer.offset, destBuffer.size, &totalSize) ||
+ totalSize > destBase->getSize()) {
+ android_errorWriteLog(0x534e4554, "176496353");
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size");
return Void();
}
@@ -157,7 +162,7 @@
}
base = static_cast<uint8_t *>(static_cast<void *>(destBase->getPointer()));
- destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset);
+ destPtr = static_cast<void*>(base + destination.nonsecureMemory.offset);
} else if (destination.type == BufferType::NATIVE_HANDLE) {
if (!secure) {
_hidl_cb(Status::BAD_VALUE, 0, "native handle destination must be secure");
@@ -170,6 +175,10 @@
_hidl_cb(Status::BAD_VALUE, 0, "invalid destination type");
return Void();
}
+
+ // release mSharedBufferLock
+ shared_buffer_lock.unlock();
+
ssize_t result = mLegacyPlugin->decrypt(secure, keyId.data(), iv.data(),
legacyMode, legacyPattern, srcPtr, legacySubSamples.get(),
subSamples.size(), destPtr, &detailMessage);
diff --git a/drm/1.0/default/CryptoPlugin.h b/drm/1.0/default/CryptoPlugin.h
index 11cc2aa..0d091fa 100644
--- a/drm/1.0/default/CryptoPlugin.h
+++ b/drm/1.0/default/CryptoPlugin.h
@@ -17,11 +17,14 @@
#ifndef ANDROID_HARDWARE_DRM_V1_0__CRYPTOPLUGIN_H
#define ANDROID_HARDWARE_DRM_V1_0__CRYPTOPLUGIN_H
-#include <android/hidl/memory/1.0/IMemory.h>
+#include <android-base/thread_annotations.h>
#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hidl/memory/1.0/IMemory.h>
#include <hidl/Status.h>
#include <media/hardware/CryptoAPI.h>
+#include <mutex>
+
namespace android {
namespace hardware {
namespace drm {
@@ -60,19 +63,21 @@
Return<void> setSharedBufferBase(const ::android::hardware::hidl_memory& base,
uint32_t bufferId) override;
- Return<void> decrypt(bool secure, const hidl_array<uint8_t, 16>& keyId,
- const hidl_array<uint8_t, 16>& iv, Mode mode, const Pattern& pattern,
- const hidl_vec<SubSample>& subSamples, const SharedBuffer& source,
- uint64_t offset, const DestinationBuffer& destination,
- decrypt_cb _hidl_cb) override;
+ Return<void> decrypt(
+ bool secure, const hidl_array<uint8_t, 16>& keyId, const hidl_array<uint8_t, 16>& iv,
+ Mode mode, const Pattern& pattern, const hidl_vec<SubSample>& subSamples,
+ const SharedBuffer& source, uint64_t offset, const DestinationBuffer& destination,
+ decrypt_cb _hidl_cb) override NO_THREAD_SAFETY_ANALYSIS; // use unique_lock
-private:
+ private:
android::CryptoPlugin *mLegacyPlugin;
- std::map<uint32_t, sp<IMemory> > mSharedBufferMap;
+ std::map<uint32_t, sp<IMemory>> mSharedBufferMap GUARDED_BY(mSharedBufferLock);
CryptoPlugin() = delete;
CryptoPlugin(const CryptoPlugin &) = delete;
void operator=(const CryptoPlugin &) = delete;
+
+ std::mutex mSharedBufferLock;
};
} // namespace implementation
diff --git a/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl b/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
index f20cd25..336e927 100644
--- a/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
+++ b/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
@@ -17,8 +17,8 @@
package android.hardware.gnss;
import android.hardware.gnss.CorrelationVector;
-import android.hardware.gnss.GnssSignalType;
import android.hardware.gnss.GnssMultipathIndicator;
+import android.hardware.gnss.GnssSignalType;
import android.hardware.gnss.SatellitePvt;
/**
@@ -32,41 +32,41 @@
@VintfStability
parcelable GnssMeasurement {
/** Bit mask indicating a valid 'snr' is stored in the GnssMeasurement. */
- const int HAS_SNR = 1 << 0;
+ const int HAS_SNR = 1 << 0;
/** Bit mask indicating a valid 'carrier frequency' is stored in the GnssMeasurement. */
- const int HAS_CARRIER_FREQUENCY = 1 << 9;
+ const int HAS_CARRIER_FREQUENCY = 1 << 9;
/** Bit mask indicating a valid 'carrier cycles' is stored in the GnssMeasurement. */
- const int HAS_CARRIER_CYCLES = 1 << 10;
+ const int HAS_CARRIER_CYCLES = 1 << 10;
/** Bit mask indicating a valid 'carrier phase' is stored in the GnssMeasurement. */
- const int HAS_CARRIER_PHASE = 1 << 11;
+ const int HAS_CARRIER_PHASE = 1 << 11;
/** Bit mask indicating a valid 'carrier phase uncertainty' is stored in the GnssMeasurement. */
- const int HAS_CARRIER_PHASE_UNCERTAINTY = 1 << 12;
+ const int HAS_CARRIER_PHASE_UNCERTAINTY = 1 << 12;
/** Bit mask indicating a valid automatic gain control is stored in the GnssMeasurement. */
- const int HAS_AUTOMATIC_GAIN_CONTROL = 1 << 13;
+ const int HAS_AUTOMATIC_GAIN_CONTROL = 1 << 13;
/** Bit mask indicating a valid full inter-signal bias is stored in the GnssMeasurement. */
- const int HAS_FULL_ISB = 1 << 16;
+ const int HAS_FULL_ISB = 1 << 16;
/**
* Bit mask indicating a valid full inter-signal bias uncertainty is stored in the
* GnssMeasurement.
*/
- const int HAS_FULL_ISB_UNCERTAINTY = 1 << 17;
+ const int HAS_FULL_ISB_UNCERTAINTY = 1 << 17;
/**
* Bit mask indicating a valid satellite inter-signal bias is stored in the GnssMeasurement.
*/
- const int HAS_SATELLITE_ISB = 1 << 18;
+ const int HAS_SATELLITE_ISB = 1 << 18;
/**
* Bit mask indicating a valid satellite inter-signal bias uncertainty is stored in the
* GnssMeasurement.
*/
- const int HAS_SATELLITE_ISB_UNCERTAINTY = 1 << 19;
+ const int HAS_SATELLITE_ISB_UNCERTAINTY = 1 << 19;
/**
* Bit mask indicating a valid satellite PVT is stored in the GnssMeasurement.
*/
- const int HAS_SATELLITE_PVT = 1 << 20;
+ const int HAS_SATELLITE_PVT = 1 << 20;
/**
* Bit mask indicating valid correlation vectors are stored in the GnssMeasurement.
*/
- const int HAS_CORRELATION_VECTOR = 1 << 21;
+ const int HAS_CORRELATION_VECTOR = 1 << 21;
/**
* A bitfield of flags indicating the validity of the fields in this GnssMeasurement. The bit
@@ -132,17 +132,17 @@
* the received GNSS satellite time value.
*
* +---------------------------+--------------------+-----+-----------+--------------------+------+
- * | |GPS/QZSS |GLNS |BDS |GAL |SBAS |
+ * | |GPS/QZSS |GLNS |BDS |GAL |SBAS |
* +---------------------------+------+------+------+-----+------+----+------+------+------+------+
* |State Flag |L1 |L5I |L5Q |L1OF |B1I |B1I |E1B |E1C |E5AQ |L1 |
- * | |C/A | | | |(D1) |(D2)| | | |C/A |
+ * | |C/A | | | |(D1) |(D2)| | | |C/A |
* |---------------------------+------+------+------+-----+------+----+------+------+------+------+
- * |STATE_UNKNOWN |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ * |STATE_UNKNOWN |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
* |---------------------------+------+------+------+-----+------+----+------+------+------+------+
* |STATE_CODE_LOCK |1ms |1 ms |1 ms |1 ms |1 ms |1 ms|- |- |1 ms |1 ms |
* |---------------------------+------+------+------+-----+------+----+------+------+------+------+
* |STATE_SYMBOL_SYNC |20ms |10 ms |1 ms |10 ms|20 ms |2 ms|4 ms |4 ms |1 ms |2 ms |
- * | |(opt.)| |(opt.)| |(opt.)| |(opt.)|(opt.)|(opt.)| |
+ * | |(opt.)| |(opt.)| |(opt.)| |(opt.)|(opt.)|(opt.)| |
* |---------------------------+------+------+------+-----+------+----+------+------+------+------+
* |STATE_BIT_SYNC |20 ms |20 ms |1 ms |20 ms|20 ms |- |8 ms |- |1 ms |4 ms |
* | | | |(opt.)| | | | | |(opt.)| |
@@ -194,24 +194,24 @@
* - For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by
* STATE_GAL_E1BC_CODE_LOCK.
*/
- const int STATE_UNKNOWN = 0;
- const int STATE_CODE_LOCK = 1 << 0;
- const int STATE_BIT_SYNC = 1 << 1;
- const int STATE_SUBFRAME_SYNC = 1 << 2;
- const int STATE_TOW_DECODED = 1 << 3;
- const int STATE_MSEC_AMBIGUOUS = 1 << 4;
- const int STATE_SYMBOL_SYNC = 1 << 5;
- const int STATE_GLO_STRING_SYNC = 1 << 6;
- const int STATE_GLO_TOD_DECODED = 1 << 7;
- const int STATE_BDS_D2_BIT_SYNC = 1 << 8;
- const int STATE_BDS_D2_SUBFRAME_SYNC = 1 << 9;
- const int STATE_GAL_E1BC_CODE_LOCK = 1 << 10;
- const int STATE_GAL_E1C_2ND_CODE_LOCK = 1 << 11;
- const int STATE_GAL_E1B_PAGE_SYNC = 1 << 12;
- const int STATE_SBAS_SYNC = 1 << 13;
- const int STATE_TOW_KNOWN = 1 << 14;
- const int STATE_GLO_TOD_KNOWN = 1 << 15;
- const int STATE_2ND_CODE_LOCK = 1 << 16;
+ const int STATE_UNKNOWN = 0;
+ const int STATE_CODE_LOCK = 1 << 0;
+ const int STATE_BIT_SYNC = 1 << 1;
+ const int STATE_SUBFRAME_SYNC = 1 << 2;
+ const int STATE_TOW_DECODED = 1 << 3;
+ const int STATE_MSEC_AMBIGUOUS = 1 << 4;
+ const int STATE_SYMBOL_SYNC = 1 << 5;
+ const int STATE_GLO_STRING_SYNC = 1 << 6;
+ const int STATE_GLO_TOD_DECODED = 1 << 7;
+ const int STATE_BDS_D2_BIT_SYNC = 1 << 8;
+ const int STATE_BDS_D2_SUBFRAME_SYNC = 1 << 9;
+ const int STATE_GAL_E1BC_CODE_LOCK = 1 << 10;
+ const int STATE_GAL_E1C_2ND_CODE_LOCK = 1 << 11;
+ const int STATE_GAL_E1B_PAGE_SYNC = 1 << 12;
+ const int STATE_SBAS_SYNC = 1 << 13;
+ const int STATE_TOW_KNOWN = 1 << 14;
+ const int STATE_GLO_TOD_KNOWN = 1 << 15;
+ const int STATE_2ND_CODE_LOCK = 1 << 16;
/**
* A bitfield of flags indicating the GnssMeasurementState per satellite sync state. It
@@ -380,7 +380,6 @@
*/
double pseudorangeRateUncertaintyMps;
-
/**
* Flags indicating the Accumulated Delta Range's states.
*
@@ -620,8 +619,10 @@
double satelliteInterSignalBiasUncertaintyNs;
/**
- * The GNSS satellite position, velocity and time information at the signal transmission time
- * receivedSvTimeInNs.
+ * The GNSS satellite position, velocity and time information at the same signal transmission
+ * time receivedSvTimeInNs.
+ *
+ * The position and velocity must be in ECEF coordinates.
*
* If the data is available, gnssMeasurementFlags must contain HAS_SATELLITE_PVT.
*/
@@ -635,4 +636,4 @@
* at equally spaced spatial offsets.
*/
CorrelationVector[] correlationVectors;
-}
\ No newline at end of file
+}
diff --git a/gnss/aidl/android/hardware/gnss/SatellitePositionEcef.aidl b/gnss/aidl/android/hardware/gnss/SatellitePositionEcef.aidl
index 4b3615e..febe623 100644
--- a/gnss/aidl/android/hardware/gnss/SatellitePositionEcef.aidl
+++ b/gnss/aidl/android/hardware/gnss/SatellitePositionEcef.aidl
@@ -18,6 +18,9 @@
/**
* Contains estimates of the satellite position fields in ECEF coordinate frame.
+ *
+ * The satellite position must be defined at the time of transmission of the
+ * signal receivedSvTimeNs.
*/
@VintfStability
parcelable SatellitePositionEcef {
@@ -36,4 +39,4 @@
* It covers satellite position and clock errors projected to the pseudorange measurements.
*/
double ureMeters;
-}
\ No newline at end of file
+}
diff --git a/gnss/aidl/android/hardware/gnss/SatelliteVelocityEcef.aidl b/gnss/aidl/android/hardware/gnss/SatelliteVelocityEcef.aidl
index 25ece3a..f2d7ab6 100644
--- a/gnss/aidl/android/hardware/gnss/SatelliteVelocityEcef.aidl
+++ b/gnss/aidl/android/hardware/gnss/SatelliteVelocityEcef.aidl
@@ -18,6 +18,9 @@
/**
* Contains estimates of the satellite velocity fields in the ECEF coordinate frame.
+ *
+ * The satellite velocity must be defined at the time of transmission of the
+ * signal receivedSvTimeNs.
*/
@VintfStability
parcelable SatelliteVelocityEcef {
@@ -37,4 +40,4 @@
* projected to the pseudorange rate measurements.
*/
double ureRateMps;
-}
\ No newline at end of file
+}
diff --git a/gnss/common/utils/default/NmeaFixInfo.cpp b/gnss/common/utils/default/NmeaFixInfo.cpp
index 43e008b..c7ee134 100644
--- a/gnss/common/utils/default/NmeaFixInfo.cpp
+++ b/gnss/common/utils/default/NmeaFixInfo.cpp
@@ -202,8 +202,15 @@
uint32_t fixId = 0;
double lastTimeStamp = 0;
for (const auto& line : nmeaRecords) {
+ if (line.compare(0, strlen(GPGA_RECORD_TAG), GPGA_RECORD_TAG) != 0 &&
+ line.compare(0, strlen(GPRMC_RECORD_TAG), GPRMC_RECORD_TAG) != 0) {
+ continue;
+ }
std::vector<std::string> sentenceValues;
splitStr(line, COMMA_SEPARATOR, sentenceValues);
+ if (sentenceValues.size() < MIN_COL_NUM) {
+ continue;
+ }
double currentTimeStamp = std::stof(sentenceValues[1]);
// If see a new timestamp, report correct location.
if ((currentTimeStamp - lastTimeStamp) > TIMESTAMP_EPSILON &&
diff --git a/gnss/common/utils/default/include/NmeaFixInfo.h b/gnss/common/utils/default/include/NmeaFixInfo.h
index 06eae7e..c96eece 100644
--- a/gnss/common/utils/default/include/NmeaFixInfo.h
+++ b/gnss/common/utils/default/include/NmeaFixInfo.h
@@ -32,6 +32,7 @@
constexpr char LINE_SEPARATOR = '\n';
constexpr char COMMA_SEPARATOR = ',';
constexpr double TIMESTAMP_EPSILON = 0.001;
+constexpr int MIN_COL_NUM = 13;
/** Helper class to parse and store the GNSS fix details information. */
class NmeaFixInfo {
diff --git a/gnss/common/utils/default/include/v2_1/GnssTemplate.h b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
index 79c78c3..a6e8f58 100644
--- a/gnss/common/utils/default/include/v2_1/GnssTemplate.h
+++ b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
@@ -196,6 +196,7 @@
return nullptr;
}
while (true) {
+ memset(inputBuffer, 0, INPUT_BUFFER_SIZE);
bytes_read = read(mGnssFd, &inputBuffer, INPUT_BUFFER_SIZE);
if (bytes_read <= 0) {
break;
@@ -218,9 +219,14 @@
auto svStatus = filterBlocklistedSatellitesV2_1(Utils::getMockSvInfoListV2_1());
this->reportSvStatus(svStatus);
auto currentLocation = getLocationFromHW();
- if (mGnssFd != -1 && currentLocation != nullptr) {
+ if (mGnssFd != -1) {
// Only report location if the return from hardware is valid
- this->reportLocation(*currentLocation);
+ // note that we can not merge these two "if" together, if didn't
+ // get location from hardware, we shouldn't report location, not
+ // report the "default" one.
+ if (currentLocation != nullptr) {
+ this->reportLocation(*currentLocation);
+ }
} else {
if (sGnssCallback_2_1 != nullptr || sGnssCallback_2_0 != nullptr) {
const auto location = Utils::getMockLocationV2_0();
@@ -259,6 +265,7 @@
if (mGnssFd != -1) {
close(mGnssFd);
mGnssFd = -1;
+ mHardwareModeChecked = false;
}
return true;
}
diff --git a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
index 3d74af4..46f5d6e 100644
--- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
+++ b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
@@ -98,7 +98,7 @@
// display change and thus the framework may want to reallocate buffers. We
// need to free all cached handles, since they are holding a strong reference
// to the underlying buffers.
- cleanDisplayResources(display);
+ cleanDisplayResources(display, mResources, mHal);
mResources->removeDisplay(display);
}
mResources->addPhysicalDisplay(display);
@@ -125,56 +125,6 @@
Hal* const mHal;
const sp<IComposerCallback> mCallback;
ComposerResources* const mResources;
-
- void cleanDisplayResources(Display display) {
- size_t cacheSize;
- Error err = mResources->getDisplayClientTargetCacheSize(display, &cacheSize);
- if (err == Error::NONE) {
- for (int slot = 0; slot < cacheSize; slot++) {
- ComposerResources::ReplacedHandle replacedBuffer(/*isBuffer*/ true);
- // Replace the buffer slots with NULLs. Keep the old handle until it is
- // replaced in ComposerHal, otherwise we risk leaving a dangling pointer.
- const native_handle_t* clientTarget = nullptr;
- err = mResources->getDisplayClientTarget(display, slot, /*useCache*/ true,
- /*rawHandle*/ nullptr, &clientTarget,
- &replacedBuffer);
- if (err != Error::NONE) {
- continue;
- }
- const std::vector<hwc_rect_t> damage;
- err = mHal->setClientTarget(display, clientTarget, /*fence*/ -1, 0, damage);
- ALOGE_IF(err != Error::NONE,
- "Can't clean slot %d of the client target buffer"
- "cache for display %" PRIu64,
- slot, display);
- }
- } else {
- ALOGE("Can't clean client target cache for display %" PRIu64, display);
- }
-
- err = mResources->getDisplayOutputBufferCacheSize(display, &cacheSize);
- if (err == Error::NONE) {
- for (int slot = 0; slot < cacheSize; slot++) {
- // Replace the buffer slots with NULLs. Keep the old handle until it is
- // replaced in ComposerHal, otherwise we risk leaving a dangling pointer.
- ComposerResources::ReplacedHandle replacedBuffer(/*isBuffer*/ true);
- const native_handle_t* outputBuffer = nullptr;
- err = mResources->getDisplayOutputBuffer(display, slot, /*useCache*/ true,
- /*rawHandle*/ nullptr, &outputBuffer,
- &replacedBuffer);
- if (err != Error::NONE) {
- continue;
- }
- err = mHal->setOutputBuffer(display, outputBuffer, /*fence*/ -1);
- ALOGE_IF(err != Error::NONE,
- "Can't clean slot %d of the output buffer cache"
- "for display %" PRIu64,
- slot, display);
- }
- } else {
- ALOGE("Can't clean output buffer cache for display %" PRIu64, display);
- }
- }
};
Return<void> registerCallback(const sp<IComposerCallback>& callback) override {
@@ -380,6 +330,57 @@
return std::make_unique<ComposerCommandEngine>(mHal, mResources.get());
}
+ static void cleanDisplayResources(Display display, ComposerResources* const resources,
+ Hal* const hal) {
+ size_t cacheSize;
+ Error err = resources->getDisplayClientTargetCacheSize(display, &cacheSize);
+ if (err == Error::NONE) {
+ for (int slot = 0; slot < cacheSize; slot++) {
+ ComposerResources::ReplacedHandle replacedBuffer(/*isBuffer*/ true);
+ // Replace the buffer slots with NULLs. Keep the old handle until it is
+ // replaced in ComposerHal, otherwise we risk leaving a dangling pointer.
+ const native_handle_t* clientTarget = nullptr;
+ err = resources->getDisplayClientTarget(display, slot, /*useCache*/ true,
+ /*rawHandle*/ nullptr, &clientTarget,
+ &replacedBuffer);
+ if (err != Error::NONE) {
+ continue;
+ }
+ const std::vector<hwc_rect_t> damage;
+ err = hal->setClientTarget(display, clientTarget, /*fence*/ -1, 0, damage);
+ ALOGE_IF(err != Error::NONE,
+ "Can't clean slot %d of the client target buffer"
+ "cache for display %" PRIu64,
+ slot, display);
+ }
+ } else {
+ ALOGE("Can't clean client target cache for display %" PRIu64, display);
+ }
+
+ err = resources->getDisplayOutputBufferCacheSize(display, &cacheSize);
+ if (err == Error::NONE) {
+ for (int slot = 0; slot < cacheSize; slot++) {
+ // Replace the buffer slots with NULLs. Keep the old handle until it is
+ // replaced in ComposerHal, otherwise we risk leaving a dangling pointer.
+ ComposerResources::ReplacedHandle replacedBuffer(/*isBuffer*/ true);
+ const native_handle_t* outputBuffer = nullptr;
+ err = resources->getDisplayOutputBuffer(display, slot, /*useCache*/ true,
+ /*rawHandle*/ nullptr, &outputBuffer,
+ &replacedBuffer);
+ if (err != Error::NONE) {
+ continue;
+ }
+ err = hal->setOutputBuffer(display, outputBuffer, /*fence*/ -1);
+ ALOGE_IF(err != Error::NONE,
+ "Can't clean slot %d of the output buffer cache"
+ "for display %" PRIu64,
+ slot, display);
+ }
+ } else {
+ ALOGE("Can't clean output buffer cache for display %" PRIu64, display);
+ }
+ }
+
void destroyResources() {
// We want to call hwc2_close here (and move hwc2_open to the
// constructor), with the assumption that hwc2_close would
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
index c889069..3464342 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
@@ -45,12 +45,21 @@
class HalEventCallback : public Hal::EventCallback_2_4 {
public:
- HalEventCallback(const sp<IComposerCallback> callback,
+ HalEventCallback(Hal* hal, const sp<IComposerCallback> callback,
V2_1::hal::ComposerResources* resources)
- : mCallback(callback), mResources(resources) {}
+ : mHal(hal), mCallback(callback), mResources(resources) {}
void onHotplug(Display display, IComposerCallback::Connection connected) override {
if (connected == IComposerCallback::Connection::CONNECTED) {
+ if (mResources->hasDisplay(display)) {
+ // This is a subsequent hotplug "connected" for a display. This signals a
+ // display change and thus the framework may want to reallocate buffers. We
+ // need to free all cached handles, since they are holding a strong reference
+ // to the underlying buffers.
+ V2_1::hal::detail::ComposerClientImpl<Interface, Hal>::cleanDisplayResources(
+ display, mResources, mHal);
+ mResources->removeDisplay(display);
+ }
mResources->addPhysicalDisplay(display);
} else if (connected == IComposerCallback::Connection::DISCONNECTED) {
mResources->removeDisplay(display);
@@ -91,13 +100,15 @@
}
protected:
+ Hal* const mHal;
const sp<IComposerCallback> mCallback;
V2_1::hal::ComposerResources* const mResources;
};
Return<void> registerCallback_2_4(const sp<IComposerCallback>& callback) override {
// no locking as we require this function to be called only once
- mHalEventCallback_2_4 = std::make_unique<HalEventCallback>(callback, mResources.get());
+ mHalEventCallback_2_4 =
+ std::make_unique<HalEventCallback>(mHal, callback, mResources.get());
mHal->registerEventCallback_2_4(mHalEventCallback_2_4.get());
return Void();
}
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
new file mode 100644
index 0000000..f35f4b7
--- /dev/null
+++ b/identity/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsIdentityTestCases"
+ },
+ {
+ "name": "VtsHalIdentityTargetTest"
+ },
+ {
+ "name": "android.hardware.identity-support-lib-test"
+ }
+ ]
+}
diff --git a/keymaster/4.1/default/Android.bp b/keymaster/4.1/default/Android.bp
index 3e2289a..6ec1fae 100644
--- a/keymaster/4.1/default/Android.bp
+++ b/keymaster/4.1/default/Android.bp
@@ -45,5 +45,14 @@
"liblog",
"libutils",
],
+ required: [
+ "android.hardware.hardware_keystore.km41.xml",
+ ],
+}
+prebuilt_etc {
+ name: "android.hardware.hardware_keystore.km41.xml",
+ sub_dir: "permissions",
+ vendor: true,
+ src: "android.hardware.hardware_keystore.km41.xml",
}
diff --git a/keymaster/4.1/default/android.hardware.hardware_keystore.km41.xml b/keymaster/4.1/default/android.hardware.hardware_keystore.km41.xml
new file mode 100644
index 0000000..0dbeed8
--- /dev/null
+++ b/keymaster/4.1/default/android.hardware.hardware_keystore.km41.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 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.
+-->
+<permissions>
+ <feature name="android.hardware.hardware_keystore" version="41" />
+</permissions>
diff --git a/light/2.0/default/Light.cpp b/light/2.0/default/Light.cpp
index 5484d2d..3febf6b 100644
--- a/light/2.0/default/Light.cpp
+++ b/light/2.0/default/Light.cpp
@@ -140,7 +140,7 @@
ret = hwModule->methods->open(hwModule, name,
reinterpret_cast<hw_device_t**>(&lightDevice));
if (ret != 0) {
- ALOGE("light_open %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
+ ALOGI("light_open %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
}
} else {
ALOGE("hw_get_module %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
diff --git a/media/c2/1.2/Android.bp b/media/c2/1.2/Android.bp
index 1094721..6d3e74d 100644
--- a/media/c2/1.2/Android.bp
+++ b/media/c2/1.2/Android.bp
@@ -1,5 +1,14 @@
// This file is autogenerated by hidl-gen -Landroidbp.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
hidl_interface {
name: "android.hardware.media.c2@1.2",
root: "android.hardware",
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
index 8329303..7849ca7 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
@@ -44,7 +44,9 @@
OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
- const nn::Request& request, nn::MeasureTiming measure) const override;
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
private:
const nn::SharedPreparedModel kPreparedModel;
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
index b695f48..1baabdf 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
@@ -22,10 +22,15 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_0::utils {
+constexpr auto kVersion = nn::Version::ANDROID_OC_MR1;
+
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
const auto maybeCanonical = nn::convert(halObject);
@@ -45,6 +50,15 @@
}
template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
auto convertFromNonCanonical(const Type& nonCanonicalObject)
-> decltype(convert(nn::convert(nonCanonicalObject).value())) {
return convert(NN_TRY(nn::convert(nonCanonicalObject)));
diff --git a/neuralnetworks/1.0/utils/src/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp
index 971ad08..e3a9757 100644
--- a/neuralnetworks/1.0/utils/src/Burst.cpp
+++ b/neuralnetworks/1.0/utils/src/Burst.cpp
@@ -20,6 +20,7 @@
#include <nnapi/IBurst.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <memory>
@@ -48,8 +49,10 @@
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute(
- const nn::Request& request, nn::MeasureTiming measure) const {
- return kPreparedModel->execute(request, measure, {}, {});
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
}
} // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
index 700b050..c0498eb 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -35,6 +35,8 @@
#include <utility>
#include <variant>
+#include "Utils.h"
+
namespace {
template <typename Type>
@@ -42,8 +44,6 @@
return static_cast<std::underlying_type_t<Type>>(value);
}
-constexpr auto kVersion = android::nn::Version::ANDROID_OC_MR1;
-
} // namespace
namespace android::nn {
@@ -53,13 +53,13 @@
using hardware::hidl_vec;
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -68,16 +68,9 @@
}
template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(hal::V1_0::utils::compliantVersion(canonical));
return canonical;
}
@@ -248,13 +241,13 @@
namespace {
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(utils::unvalidatedConvert(arguments[i]));
}
@@ -262,15 +255,8 @@
}
template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+ NN_TRY(compliantVersion(canonical));
return utils::unvalidatedConvert(canonical);
}
diff --git a/neuralnetworks/1.0/utils/test/MockDevice.h b/neuralnetworks/1.0/utils/test/MockDevice.h
index 0fb59e3..7c399ec 100644
--- a/neuralnetworks/1.0/utils/test/MockDevice.h
+++ b/neuralnetworks/1.0/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE_H
#include <android/hardware/neuralnetworks/1.0/IDevice.h>
#include <gmock/gmock.h>
@@ -83,4 +83,4 @@
} // namespace android::hardware::neuralnetworks::V1_0::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.0/utils/test/MockPreparedModel.h b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
index 7a48a83..03f1a4b 100644
--- a/neuralnetworks/1.0/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
#include <gmock/gmock.h>
@@ -82,4 +82,4 @@
} // namespace android::hardware::neuralnetworks::V1_0::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
index a5cbc72..f19ed77 100644
--- a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
@@ -224,7 +224,19 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+TEST(PreparedModelTest, configureExecutionBurst) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
TEST(PreparedModelTest, getUnderlyingResource) {
// setup test
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
index 09597a3..a8cf8cf 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
@@ -22,12 +22,16 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.1/types.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_1::utils {
constexpr auto kDefaultExecutionPreference = ExecutionPreference::FAST_SINGLE_ANSWER;
+constexpr auto kVersion = nn::Version::ANDROID_P;
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
@@ -48,6 +52,15 @@
}
template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
auto convertFromNonCanonical(const Type& nonCanonicalObject)
-> decltype(convert(nn::convert(nonCanonicalObject).value())) {
return convert(NN_TRY(nn::convert(nonCanonicalObject)));
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index d07f7d0..467ceb3 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -35,11 +35,7 @@
#include <type_traits>
#include <utility>
-namespace {
-
-constexpr auto kVersion = android::nn::Version::ANDROID_P;
-
-} // namespace
+#include "Utils.h"
namespace android::nn {
namespace {
@@ -47,13 +43,13 @@
using hardware::hidl_vec;
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -62,16 +58,9 @@
}
template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(hal::V1_1::utils::compliantVersion(canonical));
return canonical;
}
@@ -180,13 +169,13 @@
}
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
}
@@ -194,16 +183,9 @@
}
template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
- return utils::unvalidatedConvert(canonical);
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+ NN_TRY(compliantVersion(canonical));
+ return unvalidatedConvert(canonical);
}
} // anonymous namespace
diff --git a/neuralnetworks/1.1/utils/test/MockDevice.h b/neuralnetworks/1.1/utils/test/MockDevice.h
index 3b92e58..db7392d 100644
--- a/neuralnetworks/1.1/utils/test/MockDevice.h
+++ b/neuralnetworks/1.1/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
#include <android/hardware/neuralnetworks/1.1/IDevice.h>
#include <gmock/gmock.h>
@@ -92,4 +92,4 @@
} // namespace android::hardware::neuralnetworks::V1_1::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.1/utils/test/MockPreparedModel.h b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
index aba731e..257397d 100644
--- a/neuralnetworks/1.1/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
#include <gmock/gmock.h>
@@ -41,4 +41,4 @@
} // namespace android::hardware::neuralnetworks::V1_0::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
index 6b6fc71..9669d8c0 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
@@ -57,7 +57,8 @@
public:
using FallbackFunction =
std::function<nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>(
- const nn::Request&, nn::MeasureTiming)>;
+ const nn::Request&, nn::MeasureTiming, const nn::OptionalTimePoint&,
+ const nn::OptionalDuration&)>;
/**
* NN runtime memory cache.
@@ -168,7 +169,9 @@
// See IBurst::execute for information on this method.
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
- const nn::Request& request, nn::MeasureTiming measure) const override;
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
private:
mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
index 3233114..09691b6 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -22,19 +22,25 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/1.1/Utils.h>
+#include <nnapi/hal/HandleError.h>
#include <limits>
namespace android::hardware::neuralnetworks::V1_2::utils {
using CacheToken = hidl_array<uint8_t, static_cast<size_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+using V1_1::utils::kDefaultExecutionPreference;
constexpr auto kDefaultMesaureTiming = MeasureTiming::NO;
constexpr auto kNoTiming = Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
.timeInDriver = std::numeric_limits<uint64_t>::max()};
+constexpr auto kVersion = nn::Version::ANDROID_Q;
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
@@ -55,6 +61,15 @@
}
template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
auto convertFromNonCanonical(const Type& nonCanonicalObject)
-> decltype(convert(nn::convert(nonCanonicalObject).value())) {
return convert(NN_TRY(nn::convert(nonCanonicalObject)));
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 2c45583..29945b7 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -37,6 +37,8 @@
#include <type_traits>
#include <utility>
+#include "Utils.h"
+
namespace {
template <typename Type>
@@ -45,50 +47,23 @@
}
using HalDuration = std::chrono::duration<uint64_t, std::micro>;
-constexpr auto kVersion = android::nn::Version::ANDROID_Q;
-constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
} // namespace
namespace android::nn {
namespace {
-constexpr bool validOperandType(OperandType operandType) {
- switch (operandType) {
- case OperandType::FLOAT32:
- case OperandType::INT32:
- case OperandType::UINT32:
- case OperandType::TENSOR_FLOAT32:
- case OperandType::TENSOR_INT32:
- case OperandType::TENSOR_QUANT8_ASYMM:
- case OperandType::BOOL:
- case OperandType::TENSOR_QUANT16_SYMM:
- case OperandType::TENSOR_FLOAT16:
- case OperandType::TENSOR_BOOL8:
- case OperandType::FLOAT16:
- case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
- case OperandType::TENSOR_QUANT16_ASYMM:
- case OperandType::TENSOR_QUANT8_SYMM:
- case OperandType::OEM:
- case OperandType::TENSOR_OEM_BYTE:
- return true;
- default:
- break;
- }
- return isExtension(operandType);
-}
-
using hardware::hidl_handle;
using hardware::hidl_vec;
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -97,29 +72,16 @@
}
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
- const hidl_vec<Type>& arguments) {
- return unvalidatedConvertVec(arguments);
-}
-
-template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(hal::V1_2::utils::compliantVersion(canonical));
return canonical;
}
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(validatedConvert(argument)));
@@ -145,8 +107,7 @@
const bool validOperandTypes = std::all_of(
capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
[](const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
- const auto maybeType = unvalidatedConvert(operandPerformance.type);
- return !maybeType.has_value() ? false : validOperandType(maybeType.value());
+ return validatedConvert(operandPerformance.type).has_value();
});
if (!validOperandTypes) {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -275,6 +236,7 @@
GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing) {
constexpr uint64_t kMaxTiming = std::chrono::floor<HalDuration>(Duration::max()).count();
constexpr auto convertTiming = [](uint64_t halTiming) -> OptionalDuration {
+ constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
if (halTiming == kNoTiming) {
return {};
}
@@ -378,25 +340,19 @@
}
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
}
return halObject;
}
-template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
- const std::vector<Type>& arguments) {
- return unvalidatedConvertVec(arguments);
-}
-
nn::GeneralResult<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
return Operand::ExtraParams{};
}
@@ -416,22 +372,15 @@
}
template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
- return utils::unvalidatedConvert(canonical);
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+ NN_TRY(compliantVersion(canonical));
+ return unvalidatedConvert(canonical);
}
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> validatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> validatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(validatedConvert(arguments[i]));
}
@@ -469,7 +418,7 @@
capabilities.operandPerformance.asVector().end(),
std::back_inserter(operandPerformance),
[](const nn::Capabilities::OperandPerformance& operandPerformance) {
- return nn::validOperandType(operandPerformance.type);
+ return compliantVersion(operandPerformance.type).has_value();
});
return Capabilities{
@@ -570,6 +519,7 @@
nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
constexpr auto convertTiming = [](nn::OptionalDuration canonicalTiming) -> uint64_t {
+ constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
if (!canonicalTiming.has_value()) {
return kNoTiming;
}
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
index eedf591..7a17f25 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -276,7 +276,9 @@
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
-ExecutionBurstController::execute(const nn::Request& request, nn::MeasureTiming measure) const {
+ExecutionBurstController::execute(const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
// This is the first point when we know an execution is occurring, so begin to collect
// systraces. Note that the first point we can begin collecting systraces in
// ExecutionBurstServer is when the RequestChannelReceiver realizes there is data in the FMQ, so
@@ -289,7 +291,7 @@
version > nn::Version::ANDROID_Q) {
// fallback to another execution path if the packet could not be sent
if (kFallback) {
- return kFallback(request, measure);
+ return kFallback(request, measure, deadline, loopTimeoutDuration);
}
return NN_ERROR() << "Request object has features not supported by IBurst::execute";
}
@@ -323,7 +325,7 @@
if (!sendStatus.ok()) {
// fallback to another execution path if the packet could not be sent
if (kFallback) {
- return kFallback(request, measure);
+ return kFallback(request, measure, deadline, loopTimeoutDuration);
}
return NN_ERROR() << "Error sending FMQ packet: " << sendStatus.error();
}
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
index 50af881..c67159e 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
@@ -259,7 +259,7 @@
nn::MeasureTiming canonicalMeasure = NN_TRY(makeExecutionFailure(nn::convert(measure)));
const auto [outputShapes, timing] =
- NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure));
+ NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure, {}, {}));
return std::make_pair(NN_TRY(makeExecutionFailure(convert(outputShapes))),
NN_TRY(makeExecutionFailure(convert(timing))));
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
index ca3a52c..1bdde1e 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
@@ -520,6 +520,8 @@
}
return packet;
}
+
+ std::this_thread::yield();
}
// If we get to this point, we either stopped polling because it was taking too long or polling
@@ -665,6 +667,8 @@
}
return packet;
}
+
+ std::this_thread::yield();
}
// If we get to this point, we either stopped polling because it was taking too long or polling
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index 71a4ea8..b209a44 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -122,10 +122,12 @@
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
auto self = shared_from_this();
- auto fallback = [preparedModel = std::move(self)](const nn::Request& request,
- nn::MeasureTiming measure)
+ auto fallback = [preparedModel = std::move(self)](
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration)
-> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
- return preparedModel->execute(request, measure, {}, {});
+ return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
};
const auto pollingTimeWindow = getBurstControllerPollingTimeWindow();
return ExecutionBurstController::create(kPreparedModel, std::move(fallback), pollingTimeWindow);
diff --git a/neuralnetworks/1.2/utils/test/MockBurstContext.h b/neuralnetworks/1.2/utils/test/MockBurstContext.h
new file mode 100644
index 0000000..e364178
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/MockBurstContext.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
+
+#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class MockBurstContext final : public IBurstContext {
+ public:
+ // V1_2 methods below.
+ MOCK_METHOD(Return<void>, freeMemory, (int32_t slot), (override));
+};
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
diff --git a/neuralnetworks/1.2/utils/test/MockDevice.h b/neuralnetworks/1.2/utils/test/MockDevice.h
index b459943..0d34c70 100644
--- a/neuralnetworks/1.2/utils/test/MockDevice.h
+++ b/neuralnetworks/1.2/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE_H
#include <android/hardware/neuralnetworks/1.2/IDevice.h>
#include <gmock/gmock.h>
@@ -114,4 +114,4 @@
} // namespace android::hardware::neuralnetworks::V1_2::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.2/utils/test/MockPreparedModel.h b/neuralnetworks/1.2/utils/test/MockPreparedModel.h
index f5fd1f3..bd81712 100644
--- a/neuralnetworks/1.2/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.2/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
#include <gmock/gmock.h>
@@ -98,4 +98,4 @@
} // namespace android::hardware::neuralnetworks::V1_2::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
index 5062ac9..d297b1a 100644
--- a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
@@ -16,6 +16,8 @@
#include "MockPreparedModel.h"
+#include "MockBurstContext.h"
+
#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -67,6 +69,17 @@
return launchStatus;
};
}
+auto makeConfigureExecutionBurstReturn(V1_0::ErrorStatus status,
+ const sp<MockBurstContext>& burstContext) {
+ return [status, burstContext](
+ const sp<V1_2::IBurstCallback>& /*callback*/,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& /*requestChannel*/,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& /*resultChannel*/,
+ V1_2::IPreparedModel::configureExecutionBurst_cb cb) -> hardware::Return<void> {
+ cb(status, burstContext);
+ return hardware::Void();
+ };
+}
std::function<hardware::Status()> makeTransportFailure(status_t status) {
return [status] { return hardware::Status::fromStatusT(status); };
@@ -321,7 +334,76 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+TEST(PreparedModelTest, configureExecutionBurst) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto mockBurstContext = sp<MockBurstContext>::make();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::NONE, mockBurstContext));
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(
+ makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::GENERAL_FAILURE, nullptr));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
TEST(PreparedModelTest, getUnderlyingResource) {
// setup test
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
index 3ce412c..1d76caa 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
@@ -22,14 +22,25 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.3/types.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/1.1/Utils.h>
#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/Utils.h>
+#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_3::utils {
+using V1_1::utils::kDefaultExecutionPreference;
+using V1_2::utils::CacheToken;
+using V1_2::utils::kDefaultMesaureTiming;
+using V1_2::utils::kNoTiming;
+
constexpr auto kDefaultPriority = Priority::MEDIUM;
+constexpr auto kVersion = nn::Version::ANDROID_R;
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
@@ -50,6 +61,15 @@
}
template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
auto convertFromNonCanonical(const Type& nonCanonicalObject)
-> decltype(convert(nn::convert(nonCanonicalObject).value())) {
return convert(NN_TRY(nn::convert(nonCanonicalObject)));
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 9788fe1..8083ae4 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -38,6 +38,8 @@
#include <type_traits>
#include <utility>
+#include "Utils.h"
+
namespace {
template <typename Type>
@@ -45,48 +47,21 @@
return static_cast<std::underlying_type_t<Type>>(value);
}
-constexpr auto kVersion = android::nn::Version::ANDROID_R;
-
} // namespace
namespace android::nn {
namespace {
-constexpr auto validOperandType(nn::OperandType operandType) {
- switch (operandType) {
- case nn::OperandType::FLOAT32:
- case nn::OperandType::INT32:
- case nn::OperandType::UINT32:
- case nn::OperandType::TENSOR_FLOAT32:
- case nn::OperandType::TENSOR_INT32:
- case nn::OperandType::TENSOR_QUANT8_ASYMM:
- case nn::OperandType::BOOL:
- case nn::OperandType::TENSOR_QUANT16_SYMM:
- case nn::OperandType::TENSOR_FLOAT16:
- case nn::OperandType::TENSOR_BOOL8:
- case nn::OperandType::FLOAT16:
- case nn::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
- case nn::OperandType::TENSOR_QUANT16_ASYMM:
- case nn::OperandType::TENSOR_QUANT8_SYMM:
- case nn::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
- case nn::OperandType::SUBGRAPH:
- case nn::OperandType::OEM:
- case nn::OperandType::TENSOR_OEM_BYTE:
- return true;
- }
- return nn::isExtension(operandType);
-}
-
using hardware::hidl_vec;
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -95,29 +70,16 @@
}
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
- const hidl_vec<Type>& arguments) {
- return unvalidatedConvertVec(arguments);
-}
-
-template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(hal::V1_3::utils::compliantVersion(canonical));
return canonical;
}
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(validatedConvert(argument)));
@@ -143,8 +105,7 @@
const bool validOperandTypes = std::all_of(
capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
[](const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
- const auto maybeType = unvalidatedConvert(operandPerformance.type);
- return !maybeType.has_value() ? false : validOperandType(maybeType.value());
+ return validatedConvert(operandPerformance.type).has_value();
});
if (!validOperandTypes) {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -244,7 +205,7 @@
return BufferRole{
.modelIndex = bufferRole.modelIndex,
.ioIndex = bufferRole.ioIndex,
- .frequency = bufferRole.frequency,
+ .probability = bufferRole.frequency,
};
}
@@ -401,25 +362,19 @@
}
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
}
return halObject;
}
-template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
- const std::vector<Type>& arguments) {
- return unvalidatedConvertVec(arguments);
-}
-
nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::SharedMemory& memory) {
Request::MemoryPool ret;
ret.hidlMemory(NN_TRY(unvalidatedConvert(memory)));
@@ -439,22 +394,15 @@
using utils::unvalidatedConvert;
template <typename Type>
-decltype(unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+ NN_TRY(compliantVersion(canonical));
return unvalidatedConvert(canonical);
}
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> validatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> validatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(validatedConvert(arguments[i]));
}
@@ -482,7 +430,7 @@
capabilities.operandPerformance.asVector().end(),
std::back_inserter(operandPerformance),
[](const nn::Capabilities::OperandPerformance& operandPerformance) {
- return nn::validOperandType(operandPerformance.type);
+ return compliantVersion(operandPerformance.type).has_value();
});
return Capabilities{
@@ -577,7 +525,7 @@
return BufferRole{
.modelIndex = bufferRole.modelIndex,
.ioIndex = bufferRole.ioIndex,
- .frequency = bufferRole.frequency,
+ .frequency = bufferRole.probability,
};
}
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index 64275a3..fd7f8f2 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -201,10 +201,12 @@
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
auto self = shared_from_this();
- auto fallback = [preparedModel = std::move(self)](const nn::Request& request,
- nn::MeasureTiming measure)
+ auto fallback = [preparedModel = std::move(self)](
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration)
-> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
- return preparedModel->execute(request, measure, {}, {});
+ return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
};
const auto pollingTimeWindow = V1_2::utils::getBurstControllerPollingTimeWindow();
return V1_2::utils::ExecutionBurstController::create(kPreparedModel, std::move(fallback),
diff --git a/neuralnetworks/1.3/utils/test/MockBuffer.h b/neuralnetworks/1.3/utils/test/MockBuffer.h
index fb31b51..a67c5f6 100644
--- a/neuralnetworks/1.3/utils/test/MockBuffer.h
+++ b/neuralnetworks/1.3/utils/test/MockBuffer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER_H
#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
#include <gmock/gmock.h>
@@ -40,4 +40,4 @@
} // namespace android::hardware::neuralnetworks::V1_3::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/1.3/utils/test/MockBurstContext.h b/neuralnetworks/1.3/utils/test/MockBurstContext.h
new file mode 100644
index 0000000..e102b46
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockBurstContext.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
+
+#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockBurstContext final : public V1_2::IBurstContext {
+ public:
+ // V1_2 methods below.
+ MOCK_METHOD(Return<void>, freeMemory, (int32_t slot), (override));
+};
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
diff --git a/neuralnetworks/1.3/utils/test/MockDevice.h b/neuralnetworks/1.3/utils/test/MockDevice.h
index 85d3750..b79037f 100644
--- a/neuralnetworks/1.3/utils/test/MockDevice.h
+++ b/neuralnetworks/1.3/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE_H
#include <android/hardware/neuralnetworks/1.3/IDevice.h>
#include <gmock/gmock.h>
@@ -136,4 +136,4 @@
} // namespace android::hardware::neuralnetworks::V1_3::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
index fc08a7f..04c0a92 100644
--- a/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
+++ b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
#include <gmock/gmock.h>
@@ -39,4 +39,4 @@
} // namespace android::hardware::neuralnetworks::V1_3::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
diff --git a/neuralnetworks/1.3/utils/test/MockPreparedModel.h b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
index e441524..ef64fa4 100644
--- a/neuralnetworks/1.3/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
#include <gmock/gmock.h>
@@ -118,4 +118,4 @@
} // namespace android::hardware::neuralnetworks::V1_3::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
index 11796dd..5303c2a 100644
--- a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "MockBurstContext.h"
#include "MockFencedExecutionCallback.h"
#include "MockPreparedModel.h"
@@ -96,6 +97,17 @@
return hardware::Void();
};
}
+auto makeConfigureExecutionBurstReturn(V1_0::ErrorStatus status,
+ const sp<MockBurstContext>& burstContext) {
+ return [status, burstContext](
+ const sp<V1_2::IBurstCallback>& /*callback*/,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& /*requestChannel*/,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& /*resultChannel*/,
+ V1_2::IPreparedModel::configureExecutionBurst_cb cb) -> hardware::Return<void> {
+ cb(status, burstContext);
+ return hardware::Void();
+ };
+}
std::function<hardware::Status()> makeTransportFailure(status_t status) {
return [status] { return hardware::Status::fromStatusT(status); };
@@ -450,7 +462,76 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+TEST(PreparedModelTest, configureExecutionBurst) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto mockBurstContext = sp<MockBurstContext>::make();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::NONE, mockBurstContext));
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(
+ makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::GENERAL_FAILURE, nullptr));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
TEST(PreparedModelTest, getUnderlyingResource) {
// setup test
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
index f18e92a..10a6b75 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
@@ -36,5 +36,5 @@
parcelable BufferRole {
int modelIndex;
int ioIndex;
- float frequency;
+ float probability;
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
index 0d7f678..c444851 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
@@ -35,5 +35,5 @@
* used in the specified role. This is provided as a hint to optimize the case when multiple
* roles prefer different buffer locations or data layouts.
*/
- float frequency;
+ float probability;
}
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 476dac9..ad961cf 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -34,7 +34,6 @@
"libarect",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
- "neuralnetworks_utils_hal_1_0",
],
shared_libs: [
"android.hardware.neuralnetworks-V1-ndk_platform",
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
new file mode 100644
index 0000000..008e4e4
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
+
+#include <aidl/android/hardware/neuralnetworks/IBurst.h>
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <utility>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// Class that adapts aidl_hal::IBurst to nn::IBurst.
+class Burst final : public nn::IBurst {
+ struct PrivateConstructorTag {};
+
+ public:
+ /**
+ * Thread-safe, self-cleaning cache that relates an nn::Memory object to a unique int64_t
+ * identifier.
+ */
+ class MemoryCache : public std::enable_shared_from_this<MemoryCache> {
+ public:
+ using Task = std::function<void()>;
+ using Cleanup = ::android::base::ScopeGuard<Task>;
+ using SharedCleanup = std::shared_ptr<const Cleanup>;
+ using WeakCleanup = std::weak_ptr<const Cleanup>;
+
+ explicit MemoryCache(std::shared_ptr<aidl_hal::IBurst> burst);
+
+ /**
+ * Get or cache a memory object in the MemoryCache object.
+ *
+ * @param memory Memory object to be cached while the returned `SharedCleanup` is alive.
+ * @return A pair of (1) a unique identifier for the cache entry and (2) a ref-counted
+ * "hold" object which preserves the cache as long as the hold object is alive.
+ */
+ std::pair<int64_t, SharedCleanup> getOrCacheMemory(const nn::SharedMemory& memory);
+
+ /**
+ * Get a cached memory object in the MemoryCache object if it exists, otherwise
+ * std::nullopt.
+ *
+ * @param memory Memory object to be cached while the returned `SharedCleanup` is alive.
+ * @return A pair of (1) a unique identifier for the cache entry and (2) a ref-counted
+ * "hold" object which preserves the cache as long as the hold object is alive. IF the
+ * cache entry is not present, std::nullopt is returned instead.
+ */
+ std::optional<std::pair<int64_t, SharedCleanup>> getMemoryIfAvailable(
+ const nn::SharedMemory& memory);
+
+ private:
+ void tryFreeMemory(const nn::SharedMemory& memory, int64_t identifier);
+
+ const std::shared_ptr<aidl_hal::IBurst> kBurst;
+ std::mutex mMutex;
+ int64_t mUnusedIdentifier GUARDED_BY(mMutex) = 0;
+ std::unordered_map<nn::SharedMemory, std::pair<int64_t, WeakCleanup>> mCache
+ GUARDED_BY(mMutex);
+ };
+
+ static nn::GeneralResult<std::shared_ptr<const Burst>> create(
+ std::shared_ptr<aidl_hal::IBurst> burst);
+
+ Burst(PrivateConstructorTag tag, std::shared_ptr<aidl_hal::IBurst> burst);
+
+ // See IBurst::cacheMemory for information.
+ OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
+
+ // See IBurst::execute for information.
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+ private:
+ mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
+ const std::shared_ptr<aidl_hal::IBurst> kBurst;
+ const std::shared_ptr<MemoryCache> kMemoryCache;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
index 4922a6e..5eab9ff 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
@@ -99,6 +99,9 @@
const ::aidl::android::hardware::common::NativeHandle& handle);
GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence);
+GeneralResult<std::vector<Operation>> unvalidatedConvert(
+ const std::vector<aidl_hal::Operation>& operations);
+
GeneralResult<Capabilities> convert(const aidl_hal::Capabilities& capabilities);
GeneralResult<DeviceType> convert(const aidl_hal::DeviceType& deviceType);
GeneralResult<ErrorStatus> convert(const aidl_hal::ErrorStatus& errorStatus);
@@ -106,16 +109,13 @@
const aidl_hal::ExecutionPreference& executionPreference);
GeneralResult<SharedMemory> convert(const aidl_hal::Memory& memory);
GeneralResult<Model> convert(const aidl_hal::Model& model);
-GeneralResult<Operand> convert(const aidl_hal::Operand& operand);
GeneralResult<OperandType> convert(const aidl_hal::OperandType& operandType);
GeneralResult<Priority> convert(const aidl_hal::Priority& priority);
-GeneralResult<Request::MemoryPool> convert(const aidl_hal::RequestMemoryPool& memoryPool);
GeneralResult<Request> convert(const aidl_hal::Request& request);
GeneralResult<Timing> convert(const aidl_hal::Timing& timing);
GeneralResult<SyncFence> convert(const ndk::ScopedFileDescriptor& syncFence);
GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extension>& extension);
-GeneralResult<std::vector<Operation>> convert(const std::vector<aidl_hal::Operation>& outputShapes);
GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories);
GeneralResult<std::vector<OutputShape>> convert(
const std::vector<aidl_hal::OutputShape>& outputShapes);
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
index 9b28588..abce6cc 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
@@ -22,7 +22,6 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/aidl/ProtectCallback.h>
#include <memory>
#include <tuple>
@@ -35,8 +34,7 @@
namespace aidl::android::hardware::neuralnetworks::utils {
// Class that adapts aidl_hal::IPreparedModel to nn::IPreparedModel.
-class PreparedModel final : public nn::IPreparedModel,
- public std::enable_shared_from_this<PreparedModel> {
+class PreparedModel final : public nn::IPreparedModel {
struct PrivateConstructorTag {};
public:
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
index 58dcfe3..316d34f 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
#include <nnapi/hal/HandleError.h>
@@ -48,6 +49,22 @@
return result.has_value();
}
+template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(::android::hardware::neuralnetworks::utils::makeGeneralFailure(
+ nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
+auto convertFromNonCanonical(const Type& nonCanonicalObject)
+ -> decltype(convert(nn::convert(nonCanonicalObject).value())) {
+ return convert(NN_TRY(nn::convert(nonCanonicalObject)));
+}
+
nn::GeneralResult<Memory> clone(const Memory& memory);
nn::GeneralResult<Request> clone(const Request& request);
nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool);
diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp
new file mode 100644
index 0000000..0b475bc
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Burst.h"
+
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
+ const std::vector<OutputShape>& outputShapes, const Timing& timing) {
+ return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+} // namespace
+
+Burst::MemoryCache::MemoryCache(std::shared_ptr<aidl_hal::IBurst> burst)
+ : kBurst(std::move(burst)) {}
+
+std::pair<int64_t, Burst::MemoryCache::SharedCleanup> Burst::MemoryCache::getOrCacheMemory(
+ const nn::SharedMemory& memory) {
+ std::lock_guard lock(mMutex);
+
+ // Get the cache payload or create it (with default values) if it does not exist.
+ auto& cachedPayload = mCache[memory];
+ {
+ const auto& [identifier, maybeCleaner] = cachedPayload;
+ // If cache payload already exists, reuse it.
+ if (auto cleaner = maybeCleaner.lock()) {
+ return std::make_pair(identifier, std::move(cleaner));
+ }
+ }
+
+ // If the code reaches this point, the cached payload either did not exist or expired prior to
+ // this call.
+
+ // Allocate a new identifier.
+ CHECK_LT(mUnusedIdentifier, std::numeric_limits<int64_t>::max());
+ const int64_t identifier = mUnusedIdentifier++;
+
+ // Create reference-counted self-cleaning cache object.
+ auto self = weak_from_this();
+ Task cleanup = [memory, identifier, maybeMemoryCache = std::move(self)] {
+ if (const auto memoryCache = maybeMemoryCache.lock()) {
+ memoryCache->tryFreeMemory(memory, identifier);
+ }
+ };
+ auto cleaner = std::make_shared<const Cleanup>(std::move(cleanup));
+
+ // Store the result in the cache and return it.
+ auto result = std::make_pair(identifier, std::move(cleaner));
+ cachedPayload = result;
+ return result;
+}
+
+std::optional<std::pair<int64_t, Burst::MemoryCache::SharedCleanup>>
+Burst::MemoryCache::getMemoryIfAvailable(const nn::SharedMemory& memory) {
+ std::lock_guard lock(mMutex);
+
+ // Get the existing cached entry if it exists.
+ const auto iter = mCache.find(memory);
+ if (iter != mCache.end()) {
+ const auto& [identifier, maybeCleaner] = iter->second;
+ if (auto cleaner = maybeCleaner.lock()) {
+ return std::make_pair(identifier, std::move(cleaner));
+ }
+ }
+
+ // If the code reaches this point, the cached payload did not exist or was actively being
+ // deleted.
+ return std::nullopt;
+}
+
+void Burst::MemoryCache::tryFreeMemory(const nn::SharedMemory& memory, int64_t identifier) {
+ {
+ std::lock_guard guard(mMutex);
+ // Remove the cached memory and payload if it is present but expired. Note that it may not
+ // be present or may not be expired because another thread may have removed or cached the
+ // same memory object before the current thread locked mMutex in tryFreeMemory.
+ const auto iter = mCache.find(memory);
+ if (iter != mCache.end()) {
+ if (std::get<WeakCleanup>(iter->second).expired()) {
+ mCache.erase(iter);
+ }
+ }
+ }
+ kBurst->releaseMemoryResource(identifier);
+}
+
+nn::GeneralResult<std::shared_ptr<const Burst>> Burst::create(
+ std::shared_ptr<aidl_hal::IBurst> burst) {
+ if (burst == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "aidl_hal::utils::Burst::create must have non-null burst";
+ }
+
+ return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(burst));
+}
+
+Burst::Burst(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBurst> burst)
+ : kBurst(std::move(burst)), kMemoryCache(std::make_shared<MemoryCache>(kBurst)) {
+ CHECK(kBurst != nullptr);
+}
+
+Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& memory) const {
+ auto [identifier, hold] = kMemoryCache->getOrCacheMemory(memory);
+ return hold;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ // Ensure that at most one execution is in flight at any given time.
+ const bool alreadyInFlight = mExecutionInFlight.test_and_set();
+ if (alreadyInFlight) {
+ return NN_ERROR() << "IBurst already has an execution in flight";
+ }
+ const auto guard = ::android::base::make_scope_guard([this] { mExecutionInFlight.clear(); });
+
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
+ hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+
+ const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+ const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+ const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+ const auto aidlLoopTimeoutDuration =
+ NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+
+ std::vector<int64_t> memoryIdentifierTokens;
+ std::vector<OptionalCacheHold> holds;
+ memoryIdentifierTokens.reserve(request.pools.size());
+ holds.reserve(request.pools.size());
+ for (const auto& memoryPool : request.pools) {
+ if (const auto* memory = std::get_if<nn::SharedMemory>(&memoryPool)) {
+ if (auto cached = kMemoryCache->getMemoryIfAvailable(*memory)) {
+ auto& [identifier, hold] = *cached;
+ memoryIdentifierTokens.push_back(identifier);
+ holds.push_back(std::move(hold));
+ continue;
+ }
+ }
+ memoryIdentifierTokens.push_back(-1);
+ }
+ CHECK_EQ(request.pools.size(), memoryIdentifierTokens.size());
+
+ ExecutionResult executionResult;
+ const auto ret =
+ kBurst->executeSynchronously(aidlRequest, memoryIdentifierTokens, aidlMeasure,
+ aidlDeadline, aidlLoopTimeoutDuration, &executionResult);
+ HANDLE_ASTATUS(ret) << "execute failed";
+ if (!executionResult.outputSufficientSize) {
+ auto canonicalOutputShapes =
+ nn::convert(executionResult.outputShapes).value_or(std::vector<nn::OutputShape>{});
+ return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
+ << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+ }
+ auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
+ convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
+
+ NN_TRY(hal::utils::makeExecutionFailure(
+ hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
+
+ return std::make_pair(std::move(outputShapes), timing);
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index c47ba0e..c74c509 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -41,6 +41,8 @@
#include <type_traits>
#include <utility>
+#include "Utils.h"
+
#define VERIFY_NON_NEGATIVE(value) \
while (UNLIKELY(value < 0)) return NN_ERROR()
@@ -53,7 +55,6 @@
return static_cast<std::underlying_type_t<Type>>(value);
}
-constexpr auto kVersion = android::nn::Version::ANDROID_S;
constexpr int64_t kNoTiming = -1;
} // namespace
@@ -63,32 +64,6 @@
using ::aidl::android::hardware::common::NativeHandle;
-constexpr auto validOperandType(nn::OperandType operandType) {
- switch (operandType) {
- case nn::OperandType::FLOAT32:
- case nn::OperandType::INT32:
- case nn::OperandType::UINT32:
- case nn::OperandType::TENSOR_FLOAT32:
- case nn::OperandType::TENSOR_INT32:
- case nn::OperandType::TENSOR_QUANT8_ASYMM:
- case nn::OperandType::BOOL:
- case nn::OperandType::TENSOR_QUANT16_SYMM:
- case nn::OperandType::TENSOR_FLOAT16:
- case nn::OperandType::TENSOR_BOOL8:
- case nn::OperandType::FLOAT16:
- case nn::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
- case nn::OperandType::TENSOR_QUANT16_ASYMM:
- case nn::OperandType::TENSOR_QUANT8_SYMM:
- case nn::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
- case nn::OperandType::SUBGRAPH:
- return true;
- case nn::OperandType::OEM:
- case nn::OperandType::TENSOR_OEM_BYTE:
- return false;
- }
- return nn::isExtension(operandType);
-}
-
template <typename Input>
using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
@@ -113,14 +88,7 @@
template <typename Type>
GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(aidl_hal::utils::compliantVersion(canonical));
return canonical;
}
@@ -185,13 +153,21 @@
GeneralResult<OperandType> unvalidatedConvert(const aidl_hal::OperandType& operandType) {
VERIFY_NON_NEGATIVE(underlyingType(operandType)) << "Negative operand types are not allowed.";
- return static_cast<OperandType>(operandType);
+ const auto canonical = static_cast<OperandType>(operandType);
+ if (canonical == OperandType::OEM || canonical == OperandType::TENSOR_OEM_BYTE) {
+ return NN_ERROR() << "Unable to convert invalid OperandType " << canonical;
+ }
+ return canonical;
}
GeneralResult<OperationType> unvalidatedConvert(const aidl_hal::OperationType& operationType) {
VERIFY_NON_NEGATIVE(underlyingType(operationType))
<< "Negative operation types are not allowed.";
- return static_cast<OperationType>(operationType);
+ const auto canonical = static_cast<OperationType>(operationType);
+ if (canonical == OperationType::OEM_OPERATION) {
+ return NN_ERROR() << "Unable to convert invalid OperationType OEM_OPERATION";
+ }
+ return canonical;
}
GeneralResult<DeviceType> unvalidatedConvert(const aidl_hal::DeviceType& deviceType) {
@@ -206,8 +182,7 @@
const bool validOperandTypes = std::all_of(
capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
[](const aidl_hal::OperandPerformance& operandPerformance) {
- const auto maybeType = unvalidatedConvert(operandPerformance.type);
- return !maybeType.has_value() ? false : validOperandType(maybeType.value());
+ return validatedConvert(operandPerformance.type).has_value();
});
if (!validOperandTypes) {
return NN_ERROR() << "Invalid OperandType when unvalidatedConverting OperandPerformance in "
@@ -472,7 +447,7 @@
return BufferRole{
.modelIndex = static_cast<uint32_t>(bufferRole.modelIndex),
.ioIndex = static_cast<uint32_t>(bufferRole.ioIndex),
- .frequency = bufferRole.frequency,
+ .probability = bufferRole.probability,
};
}
@@ -534,6 +509,11 @@
return std::make_shared<const Handle>(NN_TRY(unvalidatedConvertHelper(aidlNativeHandle)));
}
+GeneralResult<std::vector<Operation>> unvalidatedConvert(
+ const std::vector<aidl_hal::Operation>& operations) {
+ return unvalidatedConvertVec(operations);
+}
+
GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence) {
auto duplicatedFd = NN_TRY(dupFd(syncFence.get()));
return SyncFence::create(std::move(duplicatedFd));
@@ -564,22 +544,14 @@
return validatedConvert(model);
}
-GeneralResult<Operand> convert(const aidl_hal::Operand& operand) {
- return unvalidatedConvert(operand);
-}
-
GeneralResult<OperandType> convert(const aidl_hal::OperandType& operandType) {
- return unvalidatedConvert(operandType);
+ return validatedConvert(operandType);
}
GeneralResult<Priority> convert(const aidl_hal::Priority& priority) {
return validatedConvert(priority);
}
-GeneralResult<Request::MemoryPool> convert(const aidl_hal::RequestMemoryPool& memoryPool) {
- return unvalidatedConvert(memoryPool);
-}
-
GeneralResult<Request> convert(const aidl_hal::Request& request) {
return validatedConvert(request);
}
@@ -589,17 +561,13 @@
}
GeneralResult<SyncFence> convert(const ndk::ScopedFileDescriptor& syncFence) {
- return unvalidatedConvert(syncFence);
+ return validatedConvert(syncFence);
}
GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extension>& extension) {
return validatedConvert(extension);
}
-GeneralResult<std::vector<Operation>> convert(const std::vector<aidl_hal::Operation>& operations) {
- return unvalidatedConvert(operations);
-}
-
GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories) {
return validatedConvert(memories);
}
@@ -644,14 +612,7 @@
template <typename Type>
nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(compliantVersion(canonical));
return utils::unvalidatedConvert(canonical);
}
@@ -718,7 +679,7 @@
return BufferRole{
.modelIndex = static_cast<int32_t>(bufferRole.modelIndex),
.ioIndex = static_cast<int32_t>(bufferRole.ioIndex),
- .frequency = bufferRole.frequency,
+ .probability = bufferRole.probability,
};
}
@@ -797,6 +758,9 @@
}
nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
+ if (operandType == nn::OperandType::OEM || operandType == nn::OperandType::TENSOR_OEM_BYTE) {
+ return NN_ERROR() << "Unable to convert invalid OperandType " << operandType;
+ }
return static_cast<OperandType>(operandType);
}
@@ -864,6 +828,9 @@
}
nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
+ if (operationType == nn::OperationType::OEM_OPERATION) {
+ return NN_ERROR() << "Unable to convert invalid OperationType OEM_OPERATION";
+ }
return static_cast<OperationType>(operationType);
}
@@ -1004,7 +971,7 @@
}
nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken) {
- return unvalidatedConvert(cacheToken);
+ return validatedConvert(cacheToken);
}
nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
@@ -1076,7 +1043,7 @@
nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
const std::vector<nn::SyncFence>& syncFences) {
- return unvalidatedConvert(syncFences);
+ return validatedConvert(syncFences);
}
nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& vec) {
diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
index aee4d90..003965b 100644
--- a/neuralnetworks/aidl/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -16,9 +16,9 @@
#include "PreparedModel.h"
+#include "Burst.h"
#include "Callbacks.h"
#include "Conversions.h"
-#include "ProtectCallback.h"
#include "Utils.h"
#include <android/binder_auto_utils.h>
@@ -26,7 +26,6 @@
#include <nnapi/Result.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
-#include <nnapi/hal/1.0/Burst.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
@@ -161,7 +160,10 @@
}
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
- return hal::V1_0::utils::Burst::create(shared_from_this());
+ std::shared_ptr<IBurst> burst;
+ const auto ret = kPreparedModel->configureExecutionBurst(&burst);
+ HANDLE_ASTATUS(ret) << "configureExecutionBurst failed";
+ return Burst::create(std::move(burst));
}
std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/aidl/utils/test/MockBuffer.h b/neuralnetworks/aidl/utils/test/MockBuffer.h
index 5746176..f77fa86 100644
--- a/neuralnetworks/aidl/utils/test/MockBuffer.h
+++ b/neuralnetworks/aidl/utils/test/MockBuffer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
#include <aidl/android/hardware/neuralnetworks/BnBuffer.h>
#include <android/binder_interface_utils.h>
@@ -40,4 +40,4 @@
} // namespace aidl::android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/aidl/utils/test/MockBurst.h b/neuralnetworks/aidl/utils/test/MockBurst.h
new file mode 100644
index 0000000..5083bbd
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockBurst.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
+
+#include <aidl/android/hardware/neuralnetworks/BnBurst.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockBurst final : public BnBurst {
+ public:
+ MOCK_METHOD(ndk::ScopedAStatus, executeSynchronously,
+ (const Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
+ bool measureTiming, int64_t deadline, int64_t loopTimeoutDuration,
+ ExecutionResult* executionResult),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, releaseMemoryResource, (int64_t memoryIdentifierToken),
+ (override));
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
diff --git a/neuralnetworks/aidl/utils/test/MockDevice.h b/neuralnetworks/aidl/utils/test/MockDevice.h
index 9b35bf8..3a28d55 100644
--- a/neuralnetworks/aidl/utils/test/MockDevice.h
+++ b/neuralnetworks/aidl/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
#include <android/binder_auto_utils.h>
@@ -64,4 +64,4 @@
} // namespace aidl::android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
index 463e1c9..06f9ea2 100644
--- a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
+++ b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
#include <aidl/android/hardware/neuralnetworks/BnFencedExecutionCallback.h>
#include <android/binder_auto_utils.h>
@@ -42,4 +42,4 @@
} // namespace aidl::android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
diff --git a/neuralnetworks/aidl/utils/test/MockPreparedModel.h b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
index 36e0ec3..a4ae2b7 100644
--- a/neuralnetworks/aidl/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
#include <android/binder_interface_utils.h>
@@ -49,4 +49,4 @@
} // namespace aidl::android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
index 7e28861..630a460 100644
--- a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "MockBurst.h"
#include "MockFencedExecutionCallback.h"
#include "MockPreparedModel.h"
@@ -252,7 +253,71 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+TEST(PreparedModelTest, configureExecutionBurst) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto mockBurst = ndk::SharedRefBase::make<MockBurst>();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<0>(mockBurst), Invoke(makeStatusOk)));
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
TEST(PreparedModelTest, getUnderlyingResource) {
// setup test
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index 2dd02dd..1440429 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -102,7 +102,7 @@
ASSERT_NE(result, nullptr);
// Prepare arguments.
- BufferRole role = {.modelIndex = 0, .ioIndex = index, .frequency = 1.0f};
+ BufferRole role = {.modelIndex = 0, .ioIndex = index, .probability = 1.0f};
std::vector<BufferRole> inputRoles, outputRoles;
if constexpr (ioType == IOType::INPUT) {
inputRoles = {role};
diff --git a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
index 627c26a..596f8ae 100644
--- a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
@@ -337,18 +337,18 @@
const std::shared_ptr<IPreparedModel>& model2) {
validateAllocate({
.preparedModels = {model1, model2},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.preparedModels = {model1, model2},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
- .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+ .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.preparedModels = {model1, model2},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
}
};
@@ -370,13 +370,13 @@
// Test with nullptr prepared model as input role.
validateAllocate({
.preparedModels = {nullptr},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
// Test with nullptr prepared model as output role.
validateAllocate({
.preparedModels = {nullptr},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -387,13 +387,13 @@
// Test with invalid prepared model as input role.
validateAllocate({
.preparedModels = {invalidPreparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
// Test with invalid prepared model as output role.
validateAllocate({
.preparedModels = {invalidPreparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -404,13 +404,13 @@
// This should fail, because the model index is out of bound.
validateAllocate({
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
// This should fail, because the model index is out of bound.
validateAllocate({
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -421,30 +421,30 @@
// This should fail, because the model only has one input.
validateAllocate({
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 1, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 1, .probability = 1.0f}},
});
// This should fail, because the model only has one output.
validateAllocate({
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 1, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 1, .probability = 1.0f}},
});
}
-TEST_P(MemoryDomainAllocateTest, InvalidFrequency) {
+TEST_P(MemoryDomainAllocateTest, InvalidProbability) {
auto preparedModel = createConvPreparedModel(kTestOperand);
if (preparedModel == nullptr) return;
for (float invalidFreq : {10.0f, 0.0f, -0.5f}) {
- // Test with invalid frequency for input roles.
+ // Test with invalid probability for input roles.
validateAllocate({
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = invalidFreq}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = invalidFreq}},
});
- // Test with invalid frequency for output roles.
+ // Test with invalid probability for output roles.
validateAllocate({
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = invalidFreq}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = invalidFreq}},
});
}
}
@@ -456,25 +456,25 @@
// Same role with same model index.
validateAllocate({
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
// Different model indexes, but logically referring to the same role.
validateAllocate({
.preparedModels = {preparedModel, preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.preparedModels = {preparedModel, preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -553,12 +553,12 @@
validateAllocate({
.dimensions = badDimensions,
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.dimensions = badDimensions,
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -572,12 +572,12 @@
validateAllocate({
.dimensions = badDimensions,
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.dimensions = badDimensions,
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -590,7 +590,7 @@
validateAllocate({
.dimensions = {1},
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 2, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 2, .probability = 1.0f}},
});
}
@@ -624,7 +624,7 @@
std::vector<BufferRole> inputRoles(inputIndexes.size()), outputRoles(outputIndexes.size());
auto trans = [](int32_t ind) -> BufferRole {
- return {.modelIndex = 0, .ioIndex = ind, .frequency = 1.0f};
+ return {.modelIndex = 0, .ioIndex = ind, .probability = 1.0f};
};
std::transform(inputIndexes.begin(), inputIndexes.end(), inputRoles.begin(), trans);
std::transform(outputIndexes.begin(), outputIndexes.end(), outputRoles.begin(), trans);
diff --git a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
index 6d84e1e..94d3daf 100644
--- a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
@@ -1312,7 +1312,7 @@
void validateModel(const std::shared_ptr<IDevice>& device, const Model& model) {
const auto numberOfConsumers =
nn::countNumberOfConsumers(model.main.operands.size(),
- nn::convert(model.main.operations).value())
+ nn::unvalidatedConvert(model.main.operations).value())
.value();
mutateExecutionOrderTest(device, model, numberOfConsumers);
mutateOperandTypeTest(device, model);
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
index 996858c..17b3fd9 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
@@ -32,7 +32,9 @@
OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
- const nn::Request& request, nn::MeasureTiming measure) const override;
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
};
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
index 3b87330..c92cc41 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
@@ -47,7 +47,9 @@
OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
- const nn::Request& request, nn::MeasureTiming measure) const override;
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
private:
const Factory kMakeBurst;
diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp
index 81ca18d..0c34f05 100644
--- a/neuralnetworks/utils/common/src/InvalidBurst.cpp
+++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp
@@ -32,7 +32,9 @@
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> InvalidBurst::execute(
- const nn::Request& /*request*/, nn::MeasureTiming /*measure*/) const {
+ const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+ const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
return NN_ERROR() << "InvalidBurst";
}
diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp
index 5ca868b..38ccc62 100644
--- a/neuralnetworks/utils/common/src/ResilientBurst.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBurst.cpp
@@ -100,9 +100,11 @@
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> ResilientBurst::execute(
- const nn::Request& request, nn::MeasureTiming measure) const {
- const auto fn = [&request, measure](const nn::IBurst& burst) {
- return burst.execute(request, measure);
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ const auto fn = [&request, measure, deadline, loopTimeoutDuration](const nn::IBurst& burst) {
+ return burst.execute(request, measure, deadline, loopTimeoutDuration);
};
return protect(*this, fn);
}
diff --git a/neuralnetworks/utils/common/test/MockBuffer.h b/neuralnetworks/utils/common/test/MockBuffer.h
index 59d5700..3599d0c 100644
--- a/neuralnetworks/utils/common/test/MockBuffer.h
+++ b/neuralnetworks/utils/common/test/MockBuffer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -34,4 +34,4 @@
} // namespace android::nn
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/utils/common/test/MockDevice.h b/neuralnetworks/utils/common/test/MockDevice.h
index 5566968..b274716 100644
--- a/neuralnetworks/utils/common/test/MockDevice.h
+++ b/neuralnetworks/utils/common/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -55,4 +55,4 @@
} // namespace android::nn
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h
index 418af61..c004861 100644
--- a/neuralnetworks/utils/common/test/MockPreparedModel.h
+++ b/neuralnetworks/utils/common/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -41,4 +41,4 @@
} // namespace android::nn
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL_H
diff --git a/power/aidl/Android.bp b/power/aidl/Android.bp
index 054fea5..dbd18fd 100644
--- a/power/aidl/Android.bp
+++ b/power/aidl/Android.bp
@@ -29,6 +29,9 @@
],
stability: "vintf",
backend: {
+ cpp: {
+ enabled: true,
+ },
java: {
platform_apis: true,
},
@@ -38,5 +41,7 @@
},
},
},
- versions: ["1"],
+ versions: [
+ "1",
+ ],
}
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Boost.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Boost.aidl
index aced215..c792d4e 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Boost.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Boost.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPower.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPower.aidl
index 8a06623..ae03313 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPower.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPower.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
@@ -22,4 +38,6 @@
boolean isModeSupported(in android.hardware.power.Mode type);
oneway void setBoost(in android.hardware.power.Boost type, in int durationMs);
boolean isBoostSupported(in android.hardware.power.Boost type);
+ android.hardware.power.IPowerHintSession createHintSession(in int tgid, in int uid, in int[] threadIds, in long durationNanos);
+ long getHintSessionPreferredRate();
}
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPowerHintSession.aidl
similarity index 78%
rename from biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
rename to power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPowerHintSession.aidl
index 4db47c9..1d3ecb7 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPowerHintSession.aidl
@@ -31,21 +31,12 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.biometrics.face;
-@Backing(type="byte") @VintfStability
-enum SessionState {
- IDLING = 0,
- CLOSED = 1,
- GENERATING_CHALLENGE = 2,
- REVOKING_CHALLENGE = 3,
- ENROLLING = 4,
- AUTHENTICATING = 5,
- DETECTING_INTERACTION = 6,
- ENUMERATING_ENROLLMENTS = 7,
- REMOVING_ENROLLMENTS = 8,
- GETTING_FEATURES = 9,
- SETTING_FEATURE = 10,
- GETTING_AUTHENTICATOR_ID = 11,
- INVALIDATING_AUTHENTICATOR_ID = 12,
- RESETTING_LOCKOUT = 13,
+package android.hardware.power;
+@VintfStability
+interface IPowerHintSession {
+ oneway void updateTargetWorkDuration(long targetDurationNanos);
+ oneway void reportActualWorkDuration(in android.hardware.power.WorkDuration[] durations);
+ oneway void pause();
+ oneway void resume();
+ oneway void close();
}
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
index f7c2552..8920c01 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
similarity index 78%
copy from biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
copy to power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
index 4db47c9..e86cd40 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
@@ -31,21 +31,9 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.biometrics.face;
-@Backing(type="byte") @VintfStability
-enum SessionState {
- IDLING = 0,
- CLOSED = 1,
- GENERATING_CHALLENGE = 2,
- REVOKING_CHALLENGE = 3,
- ENROLLING = 4,
- AUTHENTICATING = 5,
- DETECTING_INTERACTION = 6,
- ENUMERATING_ENROLLMENTS = 7,
- REMOVING_ENROLLMENTS = 8,
- GETTING_FEATURES = 9,
- SETTING_FEATURE = 10,
- GETTING_AUTHENTICATOR_ID = 11,
- INVALIDATING_AUTHENTICATOR_ID = 12,
- RESETTING_LOCKOUT = 13,
+package android.hardware.power;
+@VintfStability
+parcelable WorkDuration {
+ long timeStampNanos;
+ long durationNanos;
}
diff --git a/power/aidl/android/hardware/power/IPower.aidl b/power/aidl/android/hardware/power/IPower.aidl
index 2c4bd86..ee8e5a3 100644
--- a/power/aidl/android/hardware/power/IPower.aidl
+++ b/power/aidl/android/hardware/power/IPower.aidl
@@ -17,6 +17,7 @@
package android.hardware.power;
import android.hardware.power.Boost;
+import android.hardware.power.IPowerHintSession;
import android.hardware.power.Mode;
@VintfStability
@@ -69,4 +70,37 @@
* @param type Boost to be queried
*/
boolean isBoostSupported(in Boost type);
+
+ /**
+ * A Session represents a group of threads with an inter-related workload such that hints for
+ * their performance should be considered as a unit. The threads in a given session should be
+ * long-life and not created or destroyed dynamically.
+ *
+ * Each session is expected to have a periodic workload with a target duration for each
+ * cycle. The cycle duration is likely greater than the target work duration to allow other
+ * parts of the pipeline to run within the available budget. For example, a renderer thread may
+ * work at 60hz in order to produce frames at the display's frame but have a target work
+ * duration of only 6ms.
+ *
+ * Creates a session for the given set of threads and sets their initial target work
+ * duration.
+ *
+ * @return the new session if it is supported on this device, otherwise return with
+ * EX_UNSUPPORTED_OPERATION error if hint session is not supported on this device.
+ * @param tgid The TGID to be associated with this session.
+ * @param uid The UID to be associated with this session.
+ * @param threadIds The list of threads to be associated with this session.
+ * @param durationNanos The desired duration in nanoseconds for this session.
+ */
+ IPowerHintSession createHintSession(
+ in int tgid, in int uid, in int[] threadIds, in long durationNanos);
+
+ /**
+ * Get preferred update rate (interval) information for this device. Framework must communicate
+ * this rate to Apps, and also ensure the session hint sent no faster than the update rate.
+ *
+ * @return the preferred update rate in nanoseconds supported by device software. Return with
+ * EX_UNSUPPORTED_OPERATION if hint session is not supported.
+ */
+ long getHintSessionPreferredRate();
}
diff --git a/power/aidl/android/hardware/power/IPowerHintSession.aidl b/power/aidl/android/hardware/power/IPowerHintSession.aidl
new file mode 100644
index 0000000..9a85bee
--- /dev/null
+++ b/power/aidl/android/hardware/power/IPowerHintSession.aidl
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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.power;
+
+import android.hardware.power.WorkDuration;
+
+@VintfStability
+oneway interface IPowerHintSession {
+ /**
+ * Updates the desired duration of a previously-created thread group.
+ *
+ * See {@link IPowerHintSession#createHintSession} for more information on how
+ * the desired duration will be used.
+ *
+ * @param targetDurationNanos the new desired duration in nanoseconds
+ */
+ void updateTargetWorkDuration(long targetDurationNanos);
+
+ /**
+ * Reports the actual duration of a thread group.
+ *
+ * The system will attempt to adjust the core placement of the threads within
+ * the thread group and/or the frequency of the core on which they are run to bring
+ * the actual duration close to the target duration.
+ *
+ * @param actualDurationMicros how long the thread group took to complete its
+ * last task in nanoseconds
+ */
+ void reportActualWorkDuration(in WorkDuration[] durations);
+
+ /**
+ * Pause the session when the application is not in foreground and above
+ */
+ void pause();
+
+ /**
+ * Resume the session when the application is not in foreground and above
+ */
+ void resume();
+
+ /**
+ * Close the session to release resources
+ */
+ void close();
+}
diff --git a/power/aidl/android/hardware/power/WorkDuration.aidl b/power/aidl/android/hardware/power/WorkDuration.aidl
new file mode 100644
index 0000000..a06a058
--- /dev/null
+++ b/power/aidl/android/hardware/power/WorkDuration.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 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.power;
+
+@VintfStability
+parcelable WorkDuration {
+ /**
+ * Time stamp in nanoseconds based on CLOCK_MONOTONIC when the duration
+ * sample was measured.
+ */
+ long timeStampNanos;
+ /**
+ * Work duration in nanoseconds.
+ */
+ long durationNanos;
+}
diff --git a/power/aidl/default/Android.bp b/power/aidl/default/Android.bp
index 5aa6893..c0ba9a0 100644
--- a/power/aidl/default/Android.bp
+++ b/power/aidl/default/Android.bp
@@ -30,7 +30,7 @@
shared_libs: [
"libbase",
"libbinder_ndk",
- "android.hardware.power-V1-ndk_platform",
+ "android.hardware.power-V2-ndk_platform",
],
srcs: [
"main.cpp",
diff --git a/power/aidl/default/Power.cpp b/power/aidl/default/Power.cpp
index 8610de3..7f08f44 100644
--- a/power/aidl/default/Power.cpp
+++ b/power/aidl/default/Power.cpp
@@ -25,6 +25,10 @@
namespace impl {
namespace example {
+const std::vector<Boost> BOOST_RANGE{ndk::enum_range<Boost>().begin(),
+ ndk::enum_range<Boost>().end()};
+const std::vector<Mode> MODE_RANGE{ndk::enum_range<Mode>().begin(), ndk::enum_range<Mode>().end()};
+
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
LOG(VERBOSE) << "Power setMode: " << static_cast<int32_t>(type) << " to: " << enabled;
return ndk::ScopedAStatus::ok();
@@ -32,7 +36,7 @@
ndk::ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
LOG(INFO) << "Power isModeSupported: " << static_cast<int32_t>(type);
- *_aidl_return = false;
+ *_aidl_return = type >= MODE_RANGE.front() && type <= MODE_RANGE.back();
return ndk::ScopedAStatus::ok();
}
@@ -44,10 +48,21 @@
ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool* _aidl_return) {
LOG(INFO) << "Power isBoostSupported: " << static_cast<int32_t>(type);
- *_aidl_return = false;
+ *_aidl_return = type >= BOOST_RANGE.front() && type <= BOOST_RANGE.back();
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Power::createHintSession(int32_t, int32_t, const std::vector<int32_t>&, int64_t,
+ std::shared_ptr<IPowerHintSession>* _aidl_return) {
+ *_aidl_return = nullptr;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t* outNanoseconds) {
+ *outNanoseconds = -1;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
} // namespace example
} // namespace impl
} // namespace power
diff --git a/power/aidl/default/Power.h b/power/aidl/default/Power.h
index f7645aa..ef6439d 100644
--- a/power/aidl/default/Power.h
+++ b/power/aidl/default/Power.h
@@ -30,6 +30,11 @@
ndk::ScopedAStatus isModeSupported(Mode type, bool* _aidl_return) override;
ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
ndk::ScopedAStatus isBoostSupported(Boost type, bool* _aidl_return) override;
+ ndk::ScopedAStatus createHintSession(int32_t tgid, int32_t uid,
+ const std::vector<int32_t>& threadIds,
+ int64_t durationNanos,
+ std::shared_ptr<IPowerHintSession>* _aidl_return) override;
+ ndk::ScopedAStatus getHintSessionPreferredRate(int64_t* outNanoseconds) override;
};
} // namespace example
diff --git a/power/aidl/vts/Android.bp b/power/aidl/vts/Android.bp
index 1051b03..3036b82 100644
--- a/power/aidl/vts/Android.bp
+++ b/power/aidl/vts/Android.bp
@@ -29,10 +29,10 @@
],
srcs: ["VtsHalPowerTargetTest.cpp"],
shared_libs: [
- "libbinder",
+ "libbinder_ndk",
],
static_libs: [
- "android.hardware.power-V1-cpp",
+ "android.hardware.power-V2-ndk_platform",
],
test_suites: [
"vts",
diff --git a/power/aidl/vts/VtsHalPowerTargetTest.cpp b/power/aidl/vts/VtsHalPowerTargetTest.cpp
index d036c90..5bb088a 100644
--- a/power/aidl/vts/VtsHalPowerTargetTest.cpp
+++ b/power/aidl/vts/VtsHalPowerTargetTest.cpp
@@ -16,29 +16,28 @@
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
+#include <aidl/android/hardware/power/BnPower.h>
+#include <aidl/android/hardware/power/BnPowerHintSession.h>
#include <android-base/properties.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
-#include <future>
+#include <unistd.h>
-using android::ProcessState;
-using android::sp;
-using android::String16;
-using android::base::GetUintProperty;
-using android::binder::Status;
+namespace aidl::android::hardware::power {
+namespace {
+
+using ::android::base::GetUintProperty;
using android::hardware::power::Boost;
using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
using android::hardware::power::Mode;
+using android::hardware::power::WorkDuration;
-const std::vector<Boost> kBoosts{android::enum_range<Boost>().begin(),
- android::enum_range<Boost>().end()};
+const std::vector<Boost> kBoosts{ndk::enum_range<Boost>().begin(), ndk::enum_range<Boost>().end()};
-const std::vector<Mode> kModes{android::enum_range<Mode>().begin(),
- android::enum_range<Mode>().end()};
+const std::vector<Mode> kModes{ndk::enum_range<Mode>().begin(), ndk::enum_range<Mode>().end()};
const std::vector<Boost> kInvalidBoosts = {
static_cast<Boost>(static_cast<int32_t>(kBoosts.front()) - 1),
@@ -50,14 +49,48 @@
static_cast<Mode>(static_cast<int32_t>(kModes.back()) + 1),
};
+class DurationWrapper : public WorkDuration {
+ public:
+ DurationWrapper(int64_t dur, int64_t time) {
+ durationNanos = dur;
+ timeStampNanos = time;
+ }
+};
+
+const std::vector<int32_t> kSelfTids = {
+ gettid(),
+};
+
+const std::vector<int32_t> kEmptyTids = {};
+
+const std::vector<WorkDuration> kNoDurations = {};
+
+const std::vector<WorkDuration> kDurationsWithZero = {
+ DurationWrapper(1000L, 1L),
+ DurationWrapper(0L, 2L),
+};
+
+const std::vector<WorkDuration> kDurationsWithNegative = {
+ DurationWrapper(1000L, 1L),
+ DurationWrapper(-1000L, 2L),
+};
+
+const std::vector<WorkDuration> kDurations = {
+ DurationWrapper(1L, 1L),
+ DurationWrapper(1000L, 2L),
+ DurationWrapper(1000000L, 3L),
+ DurationWrapper(1000000000L, 4L),
+};
+
class PowerAidl : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
- power = android::waitForDeclaredService<IPower>(String16(GetParam().c_str()));
- ASSERT_NE(power, nullptr);
+ AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
+ ASSERT_NE(binder, nullptr);
+ power = IPower::fromBinder(ndk::SpAIBinder(binder));
}
- sp<IPower> power;
+ std::shared_ptr<IPower> power;
};
TEST_P(PowerAidl, setMode) {
@@ -110,6 +143,56 @@
}
}
+TEST_P(PowerAidl, getHintSessionPreferredRate) {
+ int64_t rate = -1;
+ auto status = power->getHintSessionPreferredRate(&rate);
+ if (!status.isOk()) {
+ ASSERT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode());
+ return;
+ }
+
+ // At least 1ms rate limit from HAL
+ ASSERT_GE(rate, 1000000);
+}
+
+TEST_P(PowerAidl, createAndCloseHintSession) {
+ std::shared_ptr<IPowerHintSession> session;
+ auto status = power->createHintSession(getpid(), getuid(), kSelfTids, 16666666L, &session);
+ if (!status.isOk()) {
+ ASSERT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode());
+ return;
+ }
+ ASSERT_NE(nullptr, session);
+ ASSERT_TRUE(session->pause().isOk());
+ ASSERT_TRUE(session->resume().isOk());
+ // Test normal destroy operation
+ ASSERT_TRUE(session->close().isOk());
+ session.reset();
+}
+TEST_P(PowerAidl, createHintSessionFailed) {
+ std::shared_ptr<IPowerHintSession> session;
+ auto status = power->createHintSession(getpid(), getuid(), kEmptyTids, 16666666L, &session);
+ ASSERT_FALSE(status.isOk());
+ if (EX_UNSUPPORTED_OPERATION == status.getExceptionCode()) {
+ return;
+ }
+ // Test with empty tid list
+ ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
+}
+
+TEST_P(PowerAidl, updateAndReportDurations) {
+ std::shared_ptr<IPowerHintSession> session;
+ auto status = power->createHintSession(getpid(), getuid(), kSelfTids, 16666666L, &session);
+ if (!status.isOk()) {
+ ASSERT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode());
+ return;
+ }
+ ASSERT_NE(nullptr, session);
+
+ ASSERT_TRUE(session->updateTargetWorkDuration(16666667LL).isOk());
+ ASSERT_TRUE(session->reportActualWorkDuration(kDurations).isOk());
+}
+
// FIXED_PERFORMANCE mode is required for all devices which ship on Android 11
// or later
TEST_P(PowerAidl, hasFixedPerformance) {
@@ -128,12 +211,16 @@
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerAidl);
INSTANTIATE_TEST_SUITE_P(Power, PowerAidl,
- testing::ValuesIn(android::getAidlHalInstanceNames(IPower::descriptor)),
- android::PrintInstanceNameToString);
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IPower::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- ProcessState::self()->setThreadPoolMaxThreadCount(1);
- ProcessState::self()->startThreadPool();
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}
+
+} // namespace aidl::android::hardware::power
diff --git a/power/stats/aidl/Android.bp b/power/stats/aidl/Android.bp
index 454c69a..0dbf9b4 100644
--- a/power/stats/aidl/Android.bp
+++ b/power/stats/aidl/Android.bp
@@ -41,4 +41,5 @@
enabled: true,
},
},
+ host_supported: true,
}
diff --git a/power/stats/aidl/default/PowerStats.cpp b/power/stats/aidl/default/PowerStats.cpp
index 7cf591e..4b771a8 100644
--- a/power/stats/aidl/default/PowerStats.cpp
+++ b/power/stats/aidl/default/PowerStats.cpp
@@ -32,15 +32,19 @@
}
int32_t id = mPowerEntityInfos.size();
+ auto info = p->getInfo();
- for (const auto& [entityName, states] : p->getInfo()) {
+ size_t index = mStateResidencyDataProviders.size();
+ mStateResidencyDataProviders.emplace_back(std::move(p));
+
+ for (const auto& [entityName, states] : info) {
PowerEntity i = {
.id = id++,
.name = entityName,
.states = states,
};
mPowerEntityInfos.emplace_back(i);
- mStateResidencyDataProviders.emplace_back(std::move(p));
+ mStateResidencyDataProviderIndex.emplace_back(index);
}
}
@@ -92,7 +96,8 @@
// Check to see if we already have data for the given id
std::string powerEntityName = mPowerEntityInfos[id].name;
if (stateResidencies.find(powerEntityName) == stateResidencies.end()) {
- mStateResidencyDataProviders[id]->getStateResidencies(&stateResidencies);
+ mStateResidencyDataProviders.at(mStateResidencyDataProviderIndex.at(id))
+ ->getStateResidencies(&stateResidencies);
}
// Append results if we have them
diff --git a/power/stats/aidl/default/PowerStats.h b/power/stats/aidl/default/PowerStats.h
index f4c5e69..91d272d 100644
--- a/power/stats/aidl/default/PowerStats.h
+++ b/power/stats/aidl/default/PowerStats.h
@@ -73,6 +73,8 @@
private:
std::vector<std::unique_ptr<IStateResidencyDataProvider>> mStateResidencyDataProviders;
std::vector<PowerEntity> mPowerEntityInfos;
+ /* Index that maps each power entity id to an entry in mStateResidencyDataProviders */
+ std::vector<size_t> mStateResidencyDataProviderIndex;
std::vector<std::unique_ptr<IEnergyConsumer>> mEnergyConsumers;
std::vector<EnergyConsumer> mEnergyConsumerInfos;
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
index d201332..a4e8811 100644
--- a/radio/1.6/IRadio.hal
+++ b/radio/1.6/IRadio.hal
@@ -544,4 +544,43 @@
* as the input param.
*/
oneway setCarrierInfoForImsiEncryption_1_6(int32_t serial, @1.6::ImsiEncryptionInfo imsiEncryptionInfo);
+
+ /**
+ * Get the local and global phonebook records from the SIM card.
+ * This should be called again after a simPhonebookChanged notification is received.
+ *
+ * The phonebook records are received via IRadioIndication.simPhonebookRecordsReceived()
+ *
+ * @param serial Serial number of request.
+ *
+ * Response callback is IRadioResponse.getSimPhonebookRecordsResponse()
+ */
+ oneway getSimPhonebookRecords(int32_t serial);
+
+ /**
+ * Get the phone book capacity
+ *
+ * @param serial Serial number of request.
+ *
+ * Response function is defined from IRadioResponse.getSimPhonebookCapacityResponse()
+ */
+ oneway getSimPhonebookCapacity(int32_t serial);
+
+ /**
+ * Insert, delete or update a phonebook record on the SIM card.
+ * If the index of recordInfo is 0, the phonebook record will be added to global or
+ * local phonebook, and global phonebook has higher priority than local phonebook.
+ *
+ * If the fields in the recordInfo are all empty except for the index, the phonebook
+ * record specified by the index will be deleted.
+ *
+ * The indication simPhonebookChanged will be called after every successful call of
+ * updateSimPhonebookRecords.
+ *
+ * @param serial Serial number of request.
+ * @param recordInfo Details of the record to insert, delete or update.
+ *
+ * Response callback is IRadioResponse.updateSimPhonebookRecordsResponse()
+ */
+ oneway updateSimPhonebookRecords(int32_t serial, PhonebookRecordInfo recordInfo);
};
diff --git a/radio/1.6/IRadioIndication.hal b/radio/1.6/IRadioIndication.hal
index a53d7c1..9788345 100644
--- a/radio/1.6/IRadioIndication.hal
+++ b/radio/1.6/IRadioIndication.hal
@@ -23,7 +23,9 @@
import @1.6::NetworkScanResult;
import @1.6::SignalStrength;
import @1.6::SetupDataCallResult;
+import @1.6::PbReceivedStatus;
import @1.6::PhysicalChannelConfig;
+import @1.6::PhonebookRecordInfo;
/**
* Interface declaring unsolicited radio indications.
@@ -72,7 +74,6 @@
*/
oneway currentLinkCapacityEstimate_1_6(RadioIndicationType type, LinkCapacityEstimate lce);
-
/**
* Indicates current signal strength of the radio.
*
@@ -113,4 +114,27 @@
*/
oneway currentPhysicalChannelConfigs_1_6(RadioIndicationType type,
vec<PhysicalChannelConfig> configs);
+
+ /**
+ * Indicates whether SIM phonebook is changed.
+ *
+ * This indication is sent whenever the SIM phonebook is changed, including SIM is
+ * inserted or removed and updated by IRadio.updateSimPhonebookRecords.
+ *
+ * @param type Type of radio indication
+ */
+ oneway simPhonebookChanged(RadioIndicationType type);
+
+ /**
+ * Indicates the content of all the used records in the SIM phonebook.
+ *
+ * This indication is associated with the API getSimPhonebookRecords and
+ * might be received more than once that is replying on the record count.
+ *
+ * @param type Type of radio indication
+ * @param status Status of PbReceivedStatus
+ * @param records Vector of PhonebookRecordInfo
+ */
+ oneway simPhonebookRecordsReceived(RadioIndicationType type,
+ PbReceivedStatus status, vec<PhonebookRecordInfo> records);
};
diff --git a/radio/1.6/IRadioResponse.hal b/radio/1.6/IRadioResponse.hal
index 805586b..33fe94f 100644
--- a/radio/1.6/IRadioResponse.hal
+++ b/radio/1.6/IRadioResponse.hal
@@ -27,6 +27,7 @@
import @1.6::SetupDataCallResult;
import @1.6::SignalStrength;
import @1.6::SlicingConfig;
+import @1.6::PhonebookCapacity;
/**
* Interface declaring response functions to solicited radio requests.
@@ -437,4 +438,57 @@
*/
oneway getSlicingConfigResponse(RadioResponseInfo info,
SlicingConfig slicingConfig);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:INVALID_ARGUMENTS
+ * RadioError:INVALID_SIM_STATE
+ * RadioError:MODEM_ERR
+ * RadioError:INTERNAL_ERR
+ * REQUEST_NOT_SUPPORTED may only be returned on devices that don't support this API,
+ * indicated by the HAL capability CAPABILITY_SIM_PHONEBOOK_IN_MODEM.
+ */
+ oneway getSimPhonebookRecordsResponse(RadioResponseInfo info);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ * @param capacity Response capacity enum indicating response processing status
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:INVALID_ARGUMENTS
+ * RadioError:INVALID_SIM_STATE
+ * RadioError:MODEM_ERR
+ * RadioError:INTERNAL_ERR
+ * REQUEST_NOT_SUPPORTED may only be returned on devices that don't support this API,
+ * indicated by the HAL capability CAPABILITY_SIM_PHONEBOOK_IN_MODEM.
+ */
+ oneway getSimPhonebookCapacityResponse(RadioResponseInfo info, PhonebookCapacity capacity);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ * @param updatedRecordIndex The index of the updated or inserted record in the phonebook and
+ * the minimum value is 1
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:INVALID_ARGUMENTS
+ * RadioError:INVALID_SIM_STATE
+ * RadioError:MODEM_ERR
+ * RadioError:INTERNAL_ERR
+ * RadioError:SIM_ERR
+ * RadioError:NO_SUCH_ENTRY
+ * RadioError:NO_RESOURCES
+ * REQUEST_NOT_SUPPORTED may only be returned on devices that don't support this API,
+ * indicated by the HAL capability CAPABILITY_SIM_PHONEBOOK_IN_MODEM.
+ */
+ oneway updateSimPhonebookRecordsResponse(RadioResponseInfo info, int32_t updatedRecordIndex);
};
diff --git a/radio/1.6/types.hal b/radio/1.6/types.hal
index f2c0e76..3fd31cc 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -1094,3 +1094,106 @@
@1.1::ImsiEncryptionInfo base;
PublicKeyType keyType; // Public key type
};
+
+/**
+ * Phonebook-record-information specified by EF_ADN(Abbreviated dialing numbers)
+ * record of SIM as per 3GPP spec 31.102 v15 Section-4.4.2.3.
+ */
+struct PhonebookRecordInfo {
+ /** Record index. 0 is used to insert a record */
+ uint32_t recordId;
+
+ /** Alpha identifier, empty string if no value */
+ string name;
+
+ /** Dialling number, empty string if no value */
+ string number;
+
+ /** Email addresses */
+ vec<string> emails;
+
+ /** Additional numbers */
+ vec<string> additionalNumbers;
+};
+
+struct PhonebookCapacity {
+ /**
+ * Maximum number of ADN records possible in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t maxAdnRecords;
+
+ /**
+ * Used ADN records in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t usedAdnRecords;
+
+ /**
+ * Maximum email records possible in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t maxEmailRecords;
+
+ /**
+ * Used email records in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t usedEmailRecords;
+
+ /**
+ * Maximum additional number records possible in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t maxAdditionalNumberRecords;
+
+ /**
+ * Used additional number records in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t usedAdditionalNumberRecords;
+
+ /**
+ * Maximum name length possible in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t maxNameLen;
+
+ /**
+ * Maximum number length possible in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t maxNumberLen;
+
+ /**
+ * Maximum email length possible in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t maxEmailLen;
+
+ /**
+ * Maximum additional number length possible in the SIM phonebook
+ * Needs to be non-negative
+ */
+ int32_t maxAdditionalNumberLen;
+};
+
+/**
+ * Enum representing the status of the received PB indication,
+ * PB_RECEIVED_OK indicates this retrieval is fine
+ * PB_RECEIVED_ERROR indicates one error happens, in general, the process
+ * can't be restored soon.
+ * PB_RECEIVED_ABORT indicates the process is interrupted, in this case,
+ * modem might need resources and interrupt the current process, or it is
+ * timed out to receive all indications, and client can retry soon.
+ * PB_RECEIVED_FINAL indicates the whole process is finished with a full
+ * chunk of phonebook data, means this is a last indication with the left
+ * data.
+ */
+enum PbReceivedStatus : int32_t {
+ PB_RECEIVED_OK = 1,
+ PB_RECEIVED_ERROR = 2,
+ PB_RECEIVED_ABORT = 3,
+ PB_RECEIVED_FINAL = 4,
+};
+
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index 70abc4b..6cf4567 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -158,6 +158,9 @@
{::android::hardware::radio::V1_6::RadioError::NONE,
::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
::android::hardware::radio::V1_6::RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW}));
+ if (radioRsp_v1_6->setupDataCallResult.trafficDescriptors.size() <= 0) {
+ return;
+ }
EXPECT_EQ(optionalTrafficDescriptor.value().osAppId.value().osAppId,
radioRsp_v1_6->setupDataCallResult.trafficDescriptors[0].osAppId.value().osAppId);
}
@@ -733,8 +736,8 @@
radio_v1_6->setCarrierInfoForImsiEncryption_1_6(serial, imsiInfo);
EXPECT_EQ(std::cv_status::no_timeout, wait());
- EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
- EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type);
+ EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial);
if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
ASSERT_TRUE(CheckAnyOfErrors(
@@ -743,3 +746,141 @@
::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
}
}
+
+/*
+ * Test IRadio.getSimPhonebookRecords() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_6, getSimPhonebookRecords) {
+ serial = GetRandomSerialNumber();
+ radio_v1_6->getSimPhonebookRecords(serial);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+ if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::INVALID_SIM_STATE,
+ ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+ ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+ ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+ ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+ CHECK_GENERAL_ERROR));
+ } else if (cardStatus.base.base.base.cardState == CardState::PRESENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::NONE,
+ ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+ CHECK_GENERAL_ERROR));
+ }
+}
+
+/*
+ * Test IRadio.getSimPhonebookCapacity for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, getSimPhonebookCapacity) {
+ serial = GetRandomSerialNumber();
+ radio_v1_6->getSimPhonebookCapacity(serial);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+ if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::INVALID_SIM_STATE,
+ ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+ ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+ ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+ ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+ CHECK_GENERAL_ERROR));
+ } else if (cardStatus.base.base.base.cardState == CardState::PRESENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::NONE,
+ ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+ CHECK_GENERAL_ERROR));
+
+ ::android::hardware::radio::V1_6::PhonebookCapacity pbCapacity =
+ radioRsp_v1_6->capacity;
+ if(pbCapacity.maxAdnRecords > 0) {
+ EXPECT_TRUE(pbCapacity.maxNameLen > 0 && pbCapacity.maxNumberLen > 0);
+ EXPECT_TRUE(pbCapacity.usedAdnRecords <= pbCapacity.maxAdnRecords);
+ }
+
+ if(pbCapacity.maxEmailRecords > 0) {
+ EXPECT_TRUE(pbCapacity.maxEmailLen > 0);
+ EXPECT_TRUE(pbCapacity.usedEmailRecords <= pbCapacity.maxEmailRecords);
+ }
+
+ if(pbCapacity.maxAdditionalNumberRecords > 0) {
+ EXPECT_TRUE(pbCapacity.maxAdditionalNumberLen > 0);
+ EXPECT_TRUE(pbCapacity.usedAdditionalNumberRecords <= pbCapacity.maxAdditionalNumberRecords);
+ }
+ }
+}
+
+/*
+ * Test IRadio.updateSimPhonebookRecords() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, updateSimPhonebookRecords) {
+ serial = GetRandomSerialNumber();
+ radio_v1_6->getSimPhonebookCapacity(serial);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+ if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::INVALID_SIM_STATE,
+ ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+ ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+ ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+ ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+ CHECK_GENERAL_ERROR));
+ } else if (cardStatus.base.base.base.cardState == CardState::PRESENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::NONE,
+ ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+ CHECK_GENERAL_ERROR));
+ ::android::hardware::radio::V1_6::PhonebookCapacity pbCapacity =
+ radioRsp_v1_6->capacity;
+
+ serial = GetRandomSerialNumber();
+ radio_v1_6->getSimPhonebookRecords(serial);
+
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+ EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+
+ if(pbCapacity.maxAdnRecords > 0
+ && pbCapacity.usedAdnRecords < pbCapacity.maxAdnRecords) {
+ // Add a phonebook record
+ PhonebookRecordInfo recordInfo;
+ recordInfo.recordId = 0;
+ recordInfo.name = "ABC";
+ recordInfo.number = "1234567890";
+ serial = GetRandomSerialNumber();
+ radio_v1_6->updateSimPhonebookRecords(serial, recordInfo);
+
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+ EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+ int index = radioRsp_v1_6->updatedRecordIndex;
+ EXPECT_TRUE(index > 0);
+
+ // Deleted a phonebook record
+ recordInfo.recordId = index;
+ recordInfo.name = "";
+ recordInfo.number = "";
+ serial = GetRandomSerialNumber();
+ radio_v1_6->updateSimPhonebookRecords(serial, recordInfo);
+
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+ EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+ }
+ }
+}
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
index 23378b5..4fc17e5 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
+++ b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
@@ -103,6 +103,11 @@
::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo> barringInfos;
RadioResponse_v1_6(RadioResponseWaiter& parent_v1_6);
+
+ // Phone Book
+ ::android::hardware::radio::V1_6::PhonebookCapacity capacity;
+ int32_t updatedRecordIndex;
+
virtual ~RadioResponse_v1_6() = default;
Return<void> getIccCardStatusResponse(
@@ -829,6 +834,17 @@
Return<void> getSlicingConfigResponse(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
const ::android::hardware::radio::V1_6::SlicingConfig& slicingConfig);
+
+ Return<void> getSimPhonebookRecordsResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+ Return<void> getSimPhonebookCapacityResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const ::android::hardware::radio::V1_6::PhonebookCapacity& capacity);
+
+ Return<void> updateSimPhonebookRecordsResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ int32_t updatedRecordIndex);
};
/* Callback class for radio indication */
@@ -1073,6 +1089,14 @@
const ::android::hardware::radio::V1_5::CellIdentity& /*cellIdentity*/,
const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo>&
/*barringInfos*/);
+
+ Return<void> simPhonebookChanged(RadioIndicationType type);
+
+ Return<void> simPhonebookRecordsReceived(
+ RadioIndicationType type,
+ ::android::hardware::radio::V1_6::PbReceivedStatus status,
+ const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::PhonebookRecordInfo>&
+ records);
};
// The main test class for Radio HIDL.
diff --git a/radio/1.6/vts/functional/radio_indication.cpp b/radio/1.6/vts/functional/radio_indication.cpp
index e7a9680..8292131 100644
--- a/radio/1.6/vts/functional/radio_indication.cpp
+++ b/radio/1.6/vts/functional/radio_indication.cpp
@@ -412,3 +412,16 @@
::android::hardware::radio::V1_6::CellInfo>& /*records*/) {
return Void();
}
+
+Return<void> RadioIndication_v1_6::simPhonebookChanged(
+ RadioIndicationType /*type*/) {
+ return Void();
+}
+
+Return<void> RadioIndication_v1_6::simPhonebookRecordsReceived(
+ RadioIndicationType /*type*/,
+ ::android::hardware::radio::V1_6::PbReceivedStatus /*status*/,
+ const ::android::hardware::hidl_vec<
+ ::android::hardware::radio::V1_6::PhonebookRecordInfo>& /*records*/) {
+ return Void();
+}
diff --git a/radio/1.6/vts/functional/radio_response.cpp b/radio/1.6/vts/functional/radio_response.cpp
index d0c2984..2b6d1bb 100644
--- a/radio/1.6/vts/functional/radio_response.cpp
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -87,7 +87,9 @@
}
Return<void> RadioResponse_v1_6::hangupConnectionResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& info) {
+ rspInfo_v1_0 = info;
+ parent_v1_6.notify(info.serial);
return Void();
}
@@ -749,7 +751,9 @@
/* 1.1 Apis */
Return<void> RadioResponse_v1_6::setCarrierInfoForImsiEncryptionResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& info) {
+ rspInfo_v1_0 = info;
+ parent_v1_6.notify(info.serial);
return Void();
}
@@ -1232,3 +1236,28 @@
parent_v1_6.notify(info.serial);
return Void();
}
+
+Return<void> RadioResponse_v1_6::getSimPhonebookRecordsResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+ rspInfo = info;
+ parent_v1_6.notify(info.serial);
+ return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSimPhonebookCapacityResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const ::android::hardware::radio::V1_6::PhonebookCapacity& capacity) {
+ rspInfo = info;
+ this->capacity = capacity;
+ parent_v1_6.notify(info.serial);
+ return Void();
+}
+
+Return<void> RadioResponse_v1_6::updateSimPhonebookRecordsResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ int32_t updatedRecordIndex) {
+ rspInfo = info;
+ this->updatedRecordIndex = updatedRecordIndex;
+ parent_v1_6.notify(info.serial);
+ return Void();
+}
diff --git a/security/keymint/aidl/OWNERS b/security/keymint/aidl/OWNERS
index 5c79db8..a93b171 100644
--- a/security/keymint/aidl/OWNERS
+++ b/security/keymint/aidl/OWNERS
@@ -1,3 +1,4 @@
+jbires@google.com
jdanis@google.com
seleneh@google.com
swillden@google.com
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
similarity index 78%
copy from biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
copy to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
index 4db47c9..d04d49c 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
@@ -31,21 +31,9 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.biometrics.face;
-@Backing(type="byte") @VintfStability
-enum SessionState {
- IDLING = 0,
- CLOSED = 1,
- GENERATING_CHALLENGE = 2,
- REVOKING_CHALLENGE = 3,
- ENROLLING = 4,
- AUTHENTICATING = 5,
- DETECTING_INTERACTION = 6,
- ENUMERATING_ENROLLMENTS = 7,
- REMOVING_ENROLLMENTS = 8,
- GETTING_FEATURES = 9,
- SETTING_FEATURE = 10,
- GETTING_AUTHENTICATOR_ID = 11,
- INVALIDATING_AUTHENTICATOR_ID = 12,
- RESETTING_LOCKOUT = 13,
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+parcelable DeviceInfo {
+ byte[] deviceInfo;
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 63bad2c..88c479c 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -36,7 +36,7 @@
@VintfStability
interface IRemotelyProvisionedComponent {
byte[] generateEcdsaP256KeyPair(in boolean testMode, out android.hardware.security.keymint.MacedPublicKey macedPublicKey);
- void generateCertificateRequest(in boolean testMode, in android.hardware.security.keymint.MacedPublicKey[] keysToSign, in byte[] endpointEncryptionCertChain, in byte[] challenge, out byte[] keysToSignMac, out android.hardware.security.keymint.ProtectedData protectedData);
+ byte[] generateCertificateRequest(in boolean testMode, in android.hardware.security.keymint.MacedPublicKey[] keysToSign, in byte[] endpointEncryptionCertChain, in byte[] challenge, out android.hardware.security.keymint.DeviceInfo deviceInfo, out android.hardware.security.keymint.ProtectedData protectedData);
const int STATUS_FAILED = 1;
const int STATUS_INVALID_MAC = 2;
const int STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 3;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
new file mode 100644
index 0000000..3ea14a1
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.keymint;
+
+/**
+ * DeviceInfo contains information about the device that's fed in as AAD in the signature of the
+ * device private key over the MAC key used for the bundle of public keys. These values are intended
+ * to be checked by the server to verify that the certificate signing request crafted by
+ * an IRemotelyProvisionedComponent HAL instance is coming from the expected device based
+ * on values initially uploaded during device manufacture in the factory.
+ * @hide
+ */
+@VintfStability
+parcelable DeviceInfo {
+ /**
+ * DeviceInfo is a CBOR Map structure described by the following CDDL.
+ *
+ * DeviceInfo = {
+ * ? "brand" : tstr,
+ * ? "manufacturer" : tstr,
+ * ? "product" : tstr,
+ * ? "model" : tstr,
+ * ? "board" : tstr,
+ * ? "vb_state" : "green" / "yellow" / "orange", // Taken from the AVB values
+ * ? "bootloader_state" : "locked" / "unlocked", // Taken from the AVB values
+ * ? "os_version" : tstr, // Same as android.os.Build.VERSION.release
+ * ? "system_patch_level" : uint, // YYYYMMDD
+ * ? "boot_patch_level" : uint, // YYYYMMDD
+ * ? "vendor_patch_level" : uint, // YYYYMMDD
+ * }
+ */
+ byte[] deviceInfo;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 17aab25..5aa3070 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -321,8 +321,8 @@
* but `attestationKey` is non-null, the IKeyMintDevice must return
* ErrorCode::INVALID_ARGUMENT. If the provided AttestationKey does not contain a key
* blob containing an asymmetric key with KeyPurpose::ATTEST_KEY, the IKeyMintDevice must
- * return ErrorCode::INVALID_PURPOSE. If the provided AttestationKey has an empty issuer
- * subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+ * return ErrorCode::INCOMPATIBLE_PURPOSE. If the provided AttestationKey has an empty
+ * issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
*
* @return The result of key creation. See KeyCreationResult.aidl.
*/
@@ -360,8 +360,8 @@
* but `attestationKey` is non-null, the IKeyMintDevice must return
* ErrorCode::INVALID_ARGUMENT. If the provided AttestationKey does not contain a key
* blob containing an asymmetric key with KeyPurpose::ATTEST_KEY, the IKeyMintDevice must
- * return ErrorCode::INVALID_PURPOSE. If the provided AttestationKey has an empty issuer
- * subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+ * return ErrorCode::INCOMPATIBLE_PURPOSE. If the provided AttestationKey has an empty
+ * issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
*
* @return The result of key creation. See KeyCreationResult.aidl.
*/
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 5c8ca6d..1ae6762 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -16,6 +16,7 @@
package android.hardware.security.keymint;
+import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.MacedPublicKey;
import android.hardware.security.keymint.ProtectedData;
@@ -201,7 +202,7 @@
* 2 : bstr // KID : EEK ID
* 3 : -25, // Algorithm : ECDH-ES + HKDF-256
* -1 : 4, // Curve : X25519
- * -2 : bstr // Ed25519 public key
+ * -2 : bstr // X25519 public key
* }
*
* EekSignatureInput = [
@@ -220,7 +221,7 @@
* in the chain, which implies that it must not attempt to validate the signature.
*
* If testMode is false, the method must validate the chain signatures, and must verify
- * that the public key in the root certifictate is in its pre-configured set of
+ * that the public key in the root certificate is in its pre-configured set of
* authorized EEK root keys. If the public key is not in the database, or if signature
* verification fails, the method must return STATUS_INVALID_EEK.
*
@@ -257,7 +258,7 @@
* @param out ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
* authenticate the keysToSign (see keysToSignMac output argument).
*/
- void generateCertificateRequest(in boolean testMode, in MacedPublicKey[] keysToSign,
- in byte[] endpointEncryptionCertChain, in byte[] challenge, out byte[] keysToSignMac,
+ byte[] generateCertificateRequest(in boolean testMode, in MacedPublicKey[] keysToSign,
+ in byte[] endpointEncryptionCertChain, in byte[] challenge, out DeviceInfo deviceInfo,
out ProtectedData protectedData);
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
index c589ca1..f3c5477 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -53,13 +53,36 @@
/**
* If the generated/imported key is an asymmetric key, `certificateChain` will contain a chain
- * of one or more certificates. If the key parameters provided to the generate/import method
- * contains Tag::ATTESTATION_CHALLENGE the first certificate will contain an attestation
- * extension, and will be signed by a factory-installed attestation key and followed by a chain
- * of certificates leading to an authoritative root. If there is no attestation challenge, only
- * one certificate will be returned, and it will be self-signed or contain a fake signature,
- * depending on whether the key has KeyPurpose::SIGN. If the generated key is symmetric,
- * certificateChain will be empty.
+ * of one or more certificates.
+ *
+ * There are a few variations in what is contained in `certificateChain`, depending on whether
+ * the caller requested attestation, whether they provided an attestation key (via the
+ * `attestationKey` parameter of `generateKey()`, `importKey()` or `importWrappedKey()`), and in
+ * the non-attestaion case, whether the key can self-sign.
+ *
+ * 1. Attestation with factory key. If Tag::ATTESTATION_CHALLENGE is provided and the
+ * `attestationKey` parameter on the generate/import call is null, the returned certificate
+ * chain must contain an attestation certificate signed with a factory-provisioned
+ * attestation key, and the full certificate chain for that factory-provisioned attestation
+ * key.
+ *
+ * 2. Attestation with caller-provided key. If Tag::ATTESTATION_CHALLENGE is provided and the
+ * `attestationKey` parameter on the generat/import call is non-null and contains the key
+ * blob of a key with KeyPurpose::ATTEST_KEY, the returned certificate chain must contain
+ * only an attestation certificate signed with the specified key. The caller must know the
+ * certificate chain for the provided key.
+ *
+ * 3. Non-attestation with signing key. If Tag::ATTESTATION_CHALLENGE is not provided and the
+ * generated/imported key has KeyPurpose::SIGN, then the returned certificate chain must
+ * contain only a single self-signed certificate with no attestation extension.
+ *
+ * 4. Non-attestation with non-signing key. If TAG::ATTESTATION_CHALLENGE is not provided and
+ * the generated/imported key does not have KeyPurpose::SIGN, then the returned certificate
+ * chain must contain only a single certificate with an empty signature and no attestation
+ * extension.
+ *
+ * 5. Symmetric key. If the generated/imported key is symmetric, the certificate chain must be
+ * empty.
*/
Certificate[] certificateChain;
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
index a26094c..62a48e9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -26,7 +26,7 @@
/**
* key is a COSE_Mac0 structure containing the new public key. It's MACed by a key available
* only to the secure environment, as proof that the public key was generated by that
- * environment. In CDDL, assuming the contained key is an Ed25519 public key:
+ * environment. In CDDL, assuming the contained key is a P-256 public key:
*
* MacedPublicKey = [ // COSE_Mac0
* protected: bstr .cbor { 1 : 5}, // Algorithm : HMAC-256
@@ -36,10 +36,11 @@
* ]
*
* PublicKey = { // COSE_Key
- * 1 : 1, // Key type : octet key pair
- * 3 : -8 // Algorithm : EdDSA
- * -1 : 6, // Curve : Ed25519
+ * 1 : 2, // Key type : EC2
+ * 3 : -8 // Algorithm : ES256
+ * -1 : 6, // Curve : P256
* -2 : bstr // X coordinate, little-endian
+ * -3 : bstr // Y coordinate, little-endian
* ? -70000 : nil // Presence indicates this is a test key. If set, K_mac is
* // all zeros.
* },
@@ -51,7 +52,7 @@
* payload : bstr .cbor PublicKey
* ]
*
- * if a non-Ed25519 public key were contained, the contents of the PublicKey map would change a
+ * if a non-P256 public key were contained, the contents of the PublicKey map would change a
* little; see RFC 8152 for details.
*/
byte[] macedKey;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
index 44f316f..5199062 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -33,7 +33,7 @@
* unprotected: {
* 5 : bstr .size 12 // IV
* },
- * ciphertext: bstr, // AES-GCM-128(K, .cbor ProtectedDataPayload)
+ * ciphertext: bstr, // AES-GCM-256(K, .cbor ProtectedDataPayload)
* recipients : [
* [ // COSE_Recipient
* protected : bstr .cbor {
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index 6243bb9..cde1fc0 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -249,8 +249,11 @@
HARDWARE_TYPE = (1 << 28) /* TagType:ENUM */ | 304,
/**
- * Keys tagged with EARLY_BOOT_ONLY may only be used, or created, during early boot, until
- * IKeyMintDevice::earlyBootEnded() is called.
+ * Keys tagged with EARLY_BOOT_ONLY may only be used during early boot, until
+ * IKeyMintDevice::earlyBootEnded() is called. Early boot keys may be created after
+ * early boot. Early boot keys may not be imprted at all, if Tag::EARLY_BOOT_ONLY is
+ * provided to IKeyMintDevice::importKey, the import must fail with
+ * ErrorCode::INVALID_ARGUMENT.
*/
EARLY_BOOT_ONLY = (7 << 28) /* TagType:BOOL */ | 305,
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
index 63b91fe..ebdc9b7 100644
--- a/security/keymint/aidl/default/Android.bp
+++ b/security/keymint/aidl/default/Android.bp
@@ -39,6 +39,17 @@
srcs: [
"service.cpp",
],
+ required: [
+ "RemoteProvisioner",
+ "android.hardware.hardware_keystore.xml",
+ ],
+}
+
+prebuilt_etc {
+ name: "android.hardware.hardware_keystore.xml",
+ sub_dir: "permissions",
+ vendor: true,
+ src: "android.hardware.hardware_keystore.xml",
}
cc_library {
diff --git a/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp b/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
index 749f0bc..5b02729 100644
--- a/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
+++ b/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
@@ -46,6 +46,14 @@
namespace {
+// Hard-coded set of acceptable public keys that can act as roots of EEK chains.
+inline const vector<bytevec> kAuthorizedEekRoots = {
+ // TODO(drysdale): replace this random value with real root pubkey(s).
+ {0x5c, 0xea, 0x4b, 0xd2, 0x31, 0x27, 0x15, 0x5e, 0x62, 0x94, 0x70,
+ 0x53, 0x94, 0x43, 0x0f, 0x9a, 0x89, 0xd5, 0xc5, 0x0f, 0x82, 0x9b,
+ 0xcd, 0x10, 0xe0, 0x79, 0xef, 0xf3, 0xfa, 0x40, 0xeb, 0x0a},
+};
+
constexpr auto STATUS_FAILED = RemotelyProvisionedComponent::STATUS_FAILED;
constexpr auto STATUS_INVALID_EEK = RemotelyProvisionedComponent::STATUS_INVALID_EEK;
constexpr auto STATUS_INVALID_MAC = RemotelyProvisionedComponent::STATUS_INVALID_MAC;
@@ -135,6 +143,13 @@
"Failed to validate EEK chain: " + cosePubKey.moveMessage());
}
lastPubKey = *std::move(cosePubKey);
+
+ // In prod mode the first pubkey should match a well-known Google public key.
+ if (!testMode && i == 0 &&
+ std::find(kAuthorizedEekRoots.begin(), kAuthorizedEekRoots.end(), lastPubKey) ==
+ kAuthorizedEekRoots.end()) {
+ return Status(STATUS_INVALID_EEK, "Unrecognized root of EEK chain");
+ }
}
auto eek = CoseKey::parseX25519(lastPubKey, true /* requireKid */);
@@ -322,8 +337,8 @@
ScopedAStatus RemotelyProvisionedComponent::generateCertificateRequest(
bool testMode, const vector<MacedPublicKey>& keysToSign,
- const bytevec& endpointEncCertChain, const bytevec& challenge, bytevec* keysToSignMac,
- ProtectedData* protectedData) {
+ const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,
+ ProtectedData* protectedData, bytevec* keysToSignMac) {
auto pubKeysToSign = validateAndExtractPubkeys(testMode, keysToSign,
testMode ? remote_prov::kTestMacKey : macKey_);
if (!pubKeysToSign.isOk()) return pubKeysToSign.moveError();
@@ -343,11 +358,13 @@
bcc = bcc_.clone();
}
+ std::unique_ptr<cppbor::Map> deviceInfoMap = createDeviceInfo();
+ deviceInfo->deviceInfo = deviceInfoMap->encode();
auto signedMac = constructCoseSign1(devicePrivKey /* Signing key */, //
ephemeralMacKey /* Payload */,
cppbor::Array() /* AAD */
.add(challenge)
- .add(createDeviceInfo())
+ .add(std::move(deviceInfoMap))
.encode());
if (!signedMac) return Status(signedMac.moveMessage());
@@ -393,8 +410,24 @@
return result;
}
-bytevec RemotelyProvisionedComponent::createDeviceInfo() const {
- return cppbor::Map().encode();
+std::unique_ptr<cppbor::Map> RemotelyProvisionedComponent::createDeviceInfo() const {
+ auto result = std::make_unique<cppbor::Map>(cppbor::Map());
+
+ // The following placeholders show how the DeviceInfo map would be populated.
+ // result->add(cppbor::Tstr("brand"), cppbor::Tstr("Google"));
+ // result->add(cppbor::Tstr("manufacturer"), cppbor::Tstr("Google"));
+ // result->add(cppbor::Tstr("product"), cppbor::Tstr("Fake"));
+ // result->add(cppbor::Tstr("model"), cppbor::Tstr("Imaginary"));
+ // result->add(cppbor::Tstr("board"), cppbor::Tstr("Chess"));
+ // result->add(cppbor::Tstr("vb_state"), cppbor::Tstr("orange"));
+ // result->add(cppbor::Tstr("bootloader_state"), cppbor::Tstr("unlocked"));
+ // result->add(cppbor::Tstr("os_version"), cppbor::Tstr("SC"));
+ // result->add(cppbor::Tstr("system_patch_level"), cppbor::Uint(20210331));
+ // result->add(cppbor::Tstr("boot_patch_level"), cppbor::Uint(20210331));
+ // result->add(cppbor::Tstr("vendor_patch_level"), cppbor::Uint(20210331));
+
+ result->canonicalize();
+ return result;
}
std::pair<bytevec /* privKey */, cppbor::Array /* BCC */>
@@ -416,8 +449,8 @@
.add(1 /* Issuer */, "Issuer")
.add(2 /* Subject */, "Subject")
.add(-4670552 /* Subject Pub Key */, coseKey)
- .add(-4670553 /* Key Usage */,
- std::vector<uint8_t>(0x05) /* Big endian order */)
+ .add(-4670553 /* Key Usage (little-endian order) */,
+ std::vector<uint8_t>{0x20} /* keyCertSign = 1<<5 */)
.canonicalize()
.encode();
auto coseSign1 = constructCoseSign1(privKey, /* signing key */
diff --git a/security/keymint/aidl/default/RemotelyProvisionedComponent.h b/security/keymint/aidl/default/RemotelyProvisionedComponent.h
index e8d2343..8185e26 100644
--- a/security/keymint/aidl/default/RemotelyProvisionedComponent.h
+++ b/security/keymint/aidl/default/RemotelyProvisionedComponent.h
@@ -39,13 +39,13 @@
const std::vector<MacedPublicKey>& keysToSign,
const std::vector<uint8_t>& endpointEncCertChain,
const std::vector<uint8_t>& challenge,
- std::vector<uint8_t>* keysToSignMac,
- ProtectedData* protectedData) override;
+ DeviceInfo* deviceInfo, ProtectedData* protectedData,
+ std::vector<uint8_t>* keysToSignMac) override;
private:
// TODO(swillden): Move these into an appropriate Context class.
std::vector<uint8_t> deriveBytesFromHbk(const std::string& context, size_t numBytes) const;
- std::vector<uint8_t> createDeviceInfo() const;
+ std::unique_ptr<cppbor::Map> createDeviceInfo() const;
std::pair<std::vector<uint8_t>, cppbor::Array> generateBcc();
std::vector<uint8_t> macKey_ = deriveBytesFromHbk("Key to MAC public keys", 32);
diff --git a/security/keymint/aidl/default/android.hardware.hardware_keystore.xml b/security/keymint/aidl/default/android.hardware.hardware_keystore.xml
new file mode 100644
index 0000000..e5a9345
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.hardware_keystore.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 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.
+-->
+<permissions>
+ <feature name="android.hardware.hardware_keystore" version="100" />
+</permissions>
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
index 991d77a..c1affa6 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -94,11 +94,14 @@
],
static_libs: [
"android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
"libcppcose",
"libgmock_ndk",
- "libremote_provisioner",
"libkeymint",
+ "libkeymint_support",
"libkeymint_remote_prov_support",
+ "libkeymint_vts_test_utils",
+ "libremote_provisioner",
],
test_suites: [
"general-tests",
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index 7e7a466..daa3e18 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -26,29 +26,6 @@
namespace {
-vector<uint8_t> make_name_from_str(const string& name) {
- X509_NAME_Ptr x509_name(X509_NAME_new());
- EXPECT_TRUE(x509_name.get() != nullptr);
- if (!x509_name) return {};
-
- EXPECT_EQ(1, X509_NAME_add_entry_by_txt(x509_name.get(), //
- "CN", //
- MBSTRING_ASC,
- reinterpret_cast<const uint8_t*>(name.c_str()),
- -1, // len
- -1, // loc
- 0 /* set */));
-
- int len = i2d_X509_NAME(x509_name.get(), nullptr /* only return length */);
- EXPECT_GT(len, 0);
-
- vector<uint8_t> retval(len);
- uint8_t* p = retval.data();
- i2d_X509_NAME(x509_name.get(), &p);
-
- return retval;
-}
-
bool IsSelfSigned(const vector<Certificate>& chain) {
if (chain.size() != 1) return false;
return ChainSignaturesAreValid(chain);
@@ -230,6 +207,36 @@
}
}
+TEST_P(AttestKeyTest, AttestWithNonAttestKey) {
+ // Create non-attestaton key.
+ AttestationKey non_attest_key;
+ vector<KeyCharacteristics> non_attest_key_characteristics;
+ vector<Certificate> non_attest_key_cert_chain;
+ ASSERT_EQ(
+ ErrorCode::OK,
+ GenerateKey(
+ AuthorizationSetBuilder().EcdsaSigningKey(EcCurve::P_256).SetDefaultValidity(),
+ {} /* attestation siging key */, &non_attest_key.keyBlob,
+ &non_attest_key_characteristics, &non_attest_key_cert_chain));
+
+ EXPECT_EQ(non_attest_key_cert_chain.size(), 1);
+ EXPECT_TRUE(IsSelfSigned(non_attest_key_cert_chain));
+
+ // Attempt to sign attestation with non-attest key.
+ vector<uint8_t> attested_key_blob;
+ vector<KeyCharacteristics> attested_key_characteristics;
+ vector<Certificate> attested_key_cert_chain;
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .SetDefaultValidity(),
+ non_attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+}
+
INSTANTIATE_KEYMINT_AIDL_TEST(AttestKeyTest);
} // 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 3e87b6b..ce6f67a 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -811,30 +811,6 @@
return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
}
-AuthorizationSet KeyMintAidlTestBase::HwEnforcedAuthorizations(
- const vector<KeyCharacteristics>& key_characteristics) {
- AuthorizationSet authList;
- for (auto& entry : key_characteristics) {
- if (entry.securityLevel == SecurityLevel::STRONGBOX ||
- entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT) {
- authList.push_back(AuthorizationSet(entry.authorizations));
- }
- }
- return authList;
-}
-
-AuthorizationSet KeyMintAidlTestBase::SwEnforcedAuthorizations(
- const vector<KeyCharacteristics>& key_characteristics) {
- AuthorizationSet authList;
- for (auto& entry : key_characteristics) {
- if (entry.securityLevel == SecurityLevel::SOFTWARE ||
- entry.securityLevel == SecurityLevel::KEYSTORE) {
- authList.push_back(AuthorizationSet(entry.authorizations));
- }
- }
- return authList;
-}
-
ErrorCode KeyMintAidlTestBase::UseAesKey(const vector<uint8_t>& aesKeyBlob) {
auto [result, ciphertext] = ProcessMessage(
aesKeyBlob, KeyPurpose::ENCRYPT, "1234567890123456",
@@ -1046,6 +1022,28 @@
return retval;
}
+AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics) {
+ AuthorizationSet authList;
+ for (auto& entry : key_characteristics) {
+ if (entry.securityLevel == SecurityLevel::STRONGBOX ||
+ entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT) {
+ authList.push_back(AuthorizationSet(entry.authorizations));
+ }
+ }
+ return authList;
+}
+
+AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics) {
+ AuthorizationSet authList;
+ for (auto& entry : key_characteristics) {
+ if (entry.securityLevel == SecurityLevel::SOFTWARE ||
+ entry.securityLevel == SecurityLevel::KEYSTORE) {
+ authList.push_back(AuthorizationSet(entry.authorizations));
+ }
+ }
+ return authList;
+}
+
AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain) {
std::stringstream cert_data;
@@ -1097,6 +1095,29 @@
return X509_Ptr(d2i_X509(nullptr /* allocate new */, &p, blob.size()));
}
+vector<uint8_t> make_name_from_str(const string& name) {
+ X509_NAME_Ptr x509_name(X509_NAME_new());
+ EXPECT_TRUE(x509_name.get() != nullptr);
+ if (!x509_name) return {};
+
+ EXPECT_EQ(1, X509_NAME_add_entry_by_txt(x509_name.get(), //
+ "CN", //
+ MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>(name.c_str()),
+ -1, // len
+ -1, // loc
+ 0 /* set */));
+
+ int len = i2d_X509_NAME(x509_name.get(), nullptr /* only return length */);
+ EXPECT_GT(len, 0);
+
+ vector<uint8_t> retval(len);
+ uint8_t* p = retval.data();
+ i2d_X509_NAME(x509_name.get(), &p);
+
+ return retval;
+}
+
} // namespace test
} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 0aef81b..4d97ea9 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -252,10 +252,6 @@
const vector<KeyParameter>& SecLevelAuthorizations(
const vector<KeyCharacteristics>& key_characteristics, SecurityLevel securityLevel);
- AuthorizationSet HwEnforcedAuthorizations(
- const vector<KeyCharacteristics>& key_characteristics);
- AuthorizationSet SwEnforcedAuthorizations(
- const vector<KeyCharacteristics>& key_characteristics);
ErrorCode UseAesKey(const vector<uint8_t>& aesKeyBlob);
ErrorCode UseHmacKey(const vector<uint8_t>& hmacKeyBlob);
ErrorCode UseRsaKey(const vector<uint8_t>& rsaKeyBlob);
@@ -280,12 +276,16 @@
const vector<uint8_t>& attestation_cert);
string bin2hex(const vector<uint8_t>& data);
X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
+vector<uint8_t> make_name_from_str(const string& name);
+AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
+AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain);
#define INSTANTIATE_KEYMINT_AIDL_TEST(name) \
INSTANTIATE_TEST_SUITE_P(PerInstance, name, \
testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
- ::android::PrintInstanceNameToString)
+ ::android::PrintInstanceNameToString); \
+ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);
} // namespace test
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 50e6cce..57bc27a 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -17,18 +17,21 @@
#define LOG_TAG "VtsRemotelyProvisionableComponentTests"
#include <RemotelyProvisionedComponent.h>
-#include <aidl/Gtest.h>
-#include <aidl/Vintf.h>
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
#include <android/binder_manager.h>
#include <cppbor_parse.h>
#include <cppcose/cppcose.h>
#include <gmock/gmock.h>
-#include <gtest/gtest.h>
#include <keymaster/keymaster_configuration.h>
+#include <keymint_support/authorization_set.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/x509.h>
#include <remote_prov/remote_prov_utils.h>
+#include "KeyMintAidlTestBase.h"
+
namespace aidl::android::hardware::security::keymint::test {
using ::std::string;
@@ -52,6 +55,214 @@
return bytevec(p, p + strlen(s));
}
+void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey) {
+ // Extract x and y affine coordinates from the encoded Cose_Key.
+ auto [parsedPayload, __, payloadParseErr] = cppbor::parse(coseKeyData);
+ ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+ auto coseKey = parsedPayload->asMap();
+ const std::unique_ptr<cppbor::Item>& xItem = coseKey->get(cppcose::CoseKey::PUBKEY_X);
+ ASSERT_NE(xItem->asBstr(), nullptr);
+ vector<uint8_t> x = xItem->asBstr()->value();
+ const std::unique_ptr<cppbor::Item>& yItem = coseKey->get(cppcose::CoseKey::PUBKEY_Y);
+ ASSERT_NE(yItem->asBstr(), nullptr);
+ vector<uint8_t> y = yItem->asBstr()->value();
+
+ // Concatenate: 0x04 (uncompressed form marker) | x | y
+ vector<uint8_t> pubKeyData{0x04};
+ pubKeyData.insert(pubKeyData.end(), x.begin(), x.end());
+ pubKeyData.insert(pubKeyData.end(), y.begin(), y.end());
+
+ EC_KEY_Ptr ecKey = EC_KEY_Ptr(EC_KEY_new());
+ ASSERT_NE(ecKey, nullptr);
+ EC_GROUP_Ptr group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ ASSERT_NE(group, nullptr);
+ ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
+ EC_POINT_Ptr point = EC_POINT_Ptr(EC_POINT_new(group.get()));
+ ASSERT_NE(point, nullptr);
+ ASSERT_EQ(EC_POINT_oct2point(group.get(), point.get(), pubKeyData.data(), pubKeyData.size(),
+ nullptr),
+ 1);
+ ASSERT_EQ(EC_KEY_set_public_key(ecKey.get(), point.get()), 1);
+
+ EVP_PKEY_Ptr pubKey = EVP_PKEY_Ptr(EVP_PKEY_new());
+ ASSERT_NE(pubKey, nullptr);
+ EVP_PKEY_assign_EC_KEY(pubKey.get(), ecKey.release());
+ *signingKey = std::move(pubKey);
+}
+
+void check_cose_key(const vector<uint8_t>& data, bool testMode) {
+ auto [parsedPayload, __, payloadParseErr] = cppbor::parse(data);
+ ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+
+ // The following check assumes that canonical CBOR encoding is used for the COSE_Key.
+ if (testMode) {
+ EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("{\n"
+ " 1 : 2,\n" // kty: EC2
+ " 3 : -7,\n" // alg: ES256
+ " -1 : 1,\n" // EC id: P256
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+ // sequence of 32 hexadecimal bytes, enclosed in braces and
+ // separated by commas. In this case, some Ed25519 public key.
+ " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
+ " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
+ " -70000 : null,\n" // test marker
+ "}"));
+ } else {
+ EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("{\n"
+ " 1 : 2,\n" // kty: EC2
+ " 3 : -7,\n" // alg: ES256
+ " -1 : 1,\n" // EC id: P256
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+ // sequence of 32 hexadecimal bytes, enclosed in braces and
+ // separated by commas. In this case, some Ed25519 public key.
+ " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
+ " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
+ "}"));
+ }
+}
+
+void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
+ vector<uint8_t>* payload_value) {
+ auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+ ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
+
+ ASSERT_NE(coseMac0->asArray(), nullptr);
+ ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
+
+ auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+ ASSERT_NE(protParms, nullptr);
+
+ // Header label:value of 'alg': HMAC-256
+ ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
+
+ auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
+ ASSERT_NE(unprotParms, nullptr);
+ ASSERT_EQ(unprotParms->size(), 0);
+
+ // The payload is a bstr holding an encoded COSE_Key
+ auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+ ASSERT_NE(payload, nullptr);
+ check_cose_key(payload->value(), testMode);
+
+ auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+ ASSERT_TRUE(coseMac0Tag);
+ auto extractedTag = coseMac0Tag->value();
+ EXPECT_EQ(extractedTag.size(), 32U);
+
+ // Compare with tag generated with kTestMacKey. Should only match in test mode
+ auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
+ payload->value());
+ ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
+
+ if (testMode) {
+ EXPECT_EQ(*testTag, extractedTag);
+ } else {
+ EXPECT_NE(*testTag, extractedTag);
+ }
+ if (payload_value != nullptr) {
+ *payload_value = payload->value();
+ }
+}
+
+ErrMsgOr<MacedPublicKey> corrupt_maced_key(const MacedPublicKey& macedPubKey) {
+ auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+ if (!coseMac0 || coseMac0->asArray()->size() != kCoseMac0EntryCount) {
+ return "COSE Mac0 parse failed";
+ }
+ auto protParams = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+ auto unprotParams = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
+ auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+ auto tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+ if (!protParams || !unprotParams || !payload || !tag) {
+ return "Invalid COSE_Sign1: missing content";
+ }
+ auto corruptMac0 = cppbor::Array();
+ corruptMac0.add(protParams->clone());
+ corruptMac0.add(unprotParams->clone());
+ corruptMac0.add(payload->clone());
+ vector<uint8_t> tagData = tag->value();
+ tagData[0] ^= 0x08;
+ tagData[tagData.size() - 1] ^= 0x80;
+ corruptMac0.add(cppbor::Bstr(tagData));
+
+ return MacedPublicKey{corruptMac0.encode()};
+}
+
+ErrMsgOr<cppbor::Array> corrupt_sig(const cppbor::Array* coseSign1) {
+ if (coseSign1->size() != kCoseSign1EntryCount) {
+ return "Invalid COSE_Sign1, wrong entry count";
+ }
+ const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
+ const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap();
+ const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr();
+ const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr();
+ if (!protectedParams || !unprotectedParams || !payload || !signature) {
+ return "Invalid COSE_Sign1: missing content";
+ }
+
+ auto corruptSig = cppbor::Array();
+ corruptSig.add(protectedParams->clone());
+ corruptSig.add(unprotectedParams->clone());
+ corruptSig.add(payload->clone());
+ vector<uint8_t> sigData = signature->value();
+ sigData[0] ^= 0x08;
+ corruptSig.add(cppbor::Bstr(sigData));
+
+ return std::move(corruptSig);
+}
+
+ErrMsgOr<EekChain> corrupt_sig_chain(const EekChain& eek, int which) {
+ auto [chain, _, parseErr] = cppbor::parse(eek.chain);
+ if (!chain || !chain->asArray()) {
+ return "EekChain parse failed";
+ }
+
+ cppbor::Array* eekChain = chain->asArray();
+ if (which >= eekChain->size()) {
+ return "selected sig out of range";
+ }
+ auto corruptChain = cppbor::Array();
+
+ for (int ii = 0; ii < eekChain->size(); ++ii) {
+ if (ii == which) {
+ auto sig = corrupt_sig(eekChain->get(which)->asArray());
+ if (!sig) {
+ return "Failed to build corrupted signature" + sig.moveMessage();
+ }
+ corruptChain.add(sig.moveValue());
+ } else {
+ corruptChain.add(eekChain->get(ii)->clone());
+ }
+ }
+ return EekChain{corruptChain.encode(), eek.last_pubkey, eek.last_privkey};
+}
+
+string device_suffix(const string& name) {
+ size_t pos = name.find('/');
+ if (pos == string::npos) {
+ return name;
+ }
+ return name.substr(pos + 1);
+}
+
+bool matching_keymint_device(const string& rp_name, std::shared_ptr<IKeyMintDevice>* keyMint) {
+ string rp_suffix = device_suffix(rp_name);
+
+ vector<string> km_names = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor);
+ for (const string& km_name : km_names) {
+ // If the suffix of the KeyMint instance equals the suffix of the
+ // RemotelyProvisionedComponent instance, assume they match.
+ if (device_suffix(km_name) == rp_suffix && AServiceManager_isDeclared(km_name.c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(km_name.c_str()));
+ *keyMint = IKeyMintDevice::fromBinder(binder);
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace
class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::string> {
@@ -78,7 +289,8 @@
INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests);
/**
- * Generate and validate a production-mode key. MAC tag can't be verified.
+ * Generate and validate a production-mode key. MAC tag can't be verified, but
+ * the private key blob should be usable in KeyMint operations.
*/
TEST_P(GenerateKeyTests, generateEcdsaP256Key_prodMode) {
MacedPublicKey macedPubKey;
@@ -86,48 +298,72 @@
bool testMode = false;
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
+ vector<uint8_t> coseKeyData;
+ check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+}
- auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
- ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
+/**
+ * Generate and validate a production-mode key, then use it as a KeyMint attestation key.
+ */
+TEST_P(GenerateKeyTests, generateAndUseEcdsaP256Key_prodMode) {
+ // See if there is a matching IKeyMintDevice for this IRemotelyProvisionedComponent.
+ std::shared_ptr<IKeyMintDevice> keyMint;
+ if (!matching_keymint_device(GetParam(), &keyMint)) {
+ // No matching IKeyMintDevice.
+ GTEST_SKIP() << "Skipping key use test as no matching KeyMint device found";
+ return;
+ }
+ KeyMintHardwareInfo info;
+ ASSERT_TRUE(keyMint->getHardwareInfo(&info).isOk());
- ASSERT_NE(coseMac0->asArray(), nullptr);
- ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
+ MacedPublicKey macedPubKey;
+ bytevec privateKeyBlob;
+ bool testMode = false;
+ auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
+ ASSERT_TRUE(status.isOk());
+ vector<uint8_t> coseKeyData;
+ check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
- auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
- ASSERT_NE(protParms, nullptr);
- ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
+ AttestationKey attestKey;
+ attestKey.keyBlob = std::move(privateKeyBlob);
+ attestKey.issuerSubjectName = make_name_from_str("Android Keystore Key");
- auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
- ASSERT_NE(unprotParms, nullptr);
- ASSERT_EQ(unprotParms->size(), 0);
+ // Generate an ECDSA key that is attested by the generated P256 keypair.
+ AuthorizationSet keyDesc = AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .Digest(Digest::NONE)
+ .SetDefaultValidity();
+ KeyCreationResult creationResult;
+ auto result = keyMint->generateKey(keyDesc.vector_data(), attestKey, &creationResult);
+ ASSERT_TRUE(result.isOk());
+ vector<uint8_t> attested_key_blob = std::move(creationResult.keyBlob);
+ vector<KeyCharacteristics> attested_key_characteristics =
+ std::move(creationResult.keyCharacteristics);
+ vector<Certificate> attested_key_cert_chain = std::move(creationResult.certificateChain);
+ EXPECT_EQ(attested_key_cert_chain.size(), 1);
- auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
- ASSERT_NE(payload, nullptr);
- auto [parsedPayload, __, payloadParseErr] = cppbor::parse(payload->value());
- ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
- EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
- MatchesRegex("{\n"
- " 1 : 2,\n"
- " 3 : -7,\n"
- " -1 : 1,\n"
- // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a sequence of
- // 32 hexadecimal bytes, enclosed in braces and separated by commas.
- // In this case, some Ed25519 public key.
- " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
- " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
- "}"));
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced,
+ info.securityLevel,
+ attested_key_cert_chain[0].encodedCertificate));
- auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
- ASSERT_TRUE(coseMac0Tag);
- auto extractedTag = coseMac0Tag->value();
- EXPECT_EQ(extractedTag.size(), 32U);
+ // Attestation by itself is not valid (last entry is not self-signed).
+ EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
- // Compare with tag generated with kTestMacKey. Shouldn't match.
- auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
- payload->value());
- ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
+ // The signature over the attested key should correspond to the P256 public key.
+ X509_Ptr key_cert(parse_cert_blob(attested_key_cert_chain[0].encodedCertificate));
+ ASSERT_TRUE(key_cert.get());
+ EVP_PKEY_Ptr signing_pubkey;
+ p256_pub_key(coseKeyData, &signing_pubkey);
+ ASSERT_TRUE(signing_pubkey.get());
- EXPECT_NE(*testTag, extractedTag);
+ ASSERT_TRUE(X509_verify(key_cert.get(), signing_pubkey.get()))
+ << "Verification of attested certificate failed "
+ << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
}
/**
@@ -140,56 +376,20 @@
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
- auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
- ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
-
- ASSERT_NE(coseMac0->asArray(), nullptr);
- ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
-
- auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
- ASSERT_NE(protParms, nullptr);
- ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
-
- auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
- ASSERT_NE(unprotParms, nullptr);
- ASSERT_EQ(unprotParms->size(), 0);
-
- auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
- ASSERT_NE(payload, nullptr);
- auto [parsedPayload, __, payloadParseErr] = cppbor::parse(payload->value());
- ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
- EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
- MatchesRegex("{\n"
- " 1 : 2,\n"
- " 3 : -7,\n"
- " -1 : 1,\n"
- // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a sequence of
- // 32 hexadecimal bytes, enclosed in braces and separated by commas.
- // In this case, some Ed25519 public key.
- " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
- " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
- " -70000 : null,\n"
- "}"));
-
- auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
- ASSERT_TRUE(coseMac0);
- auto extractedTag = coseMac0Tag->value();
- EXPECT_EQ(extractedTag.size(), 32U);
-
- // Compare with tag generated with kTestMacKey. Should match.
- auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
- payload->value());
- ASSERT_TRUE(testTag) << testTag.message();
-
- EXPECT_EQ(*testTag, extractedTag);
+ check_maced_pubkey(macedPubKey, testMode, nullptr);
}
class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
protected:
- CertificateRequestTest() : eekId_(string_to_bytevec("eekid")) {
- auto chain = generateEekChain(3, eekId_);
+ CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) {
+ generateEek(3);
+ }
+
+ void generateEek(size_t eekLength) {
+ auto chain = generateEekChain(eekLength, eekId_);
EXPECT_TRUE(chain) << chain.message();
if (chain) eekChain_ = chain.moveValue();
+ eekLength_ = eekLength;
}
void generateKeys(bool testMode, size_t numKeys) {
@@ -201,21 +401,76 @@
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &key, &privateKeyBlob);
ASSERT_TRUE(status.isOk()) << status.getMessage();
- auto [parsedMacedKey, _, parseErr] = cppbor::parse(key.macedKey);
- ASSERT_TRUE(parsedMacedKey) << "Failed parsing MACed key: " << parseErr;
- ASSERT_TRUE(parsedMacedKey->asArray()) << "COSE_Mac0 not an array?";
- ASSERT_EQ(parsedMacedKey->asArray()->size(), kCoseMac0EntryCount);
-
- auto& payload = parsedMacedKey->asArray()->get(kCoseMac0Payload);
- ASSERT_TRUE(payload);
- ASSERT_TRUE(payload->asBstr());
-
- cborKeysToSign_.add(cppbor::EncodedItem(payload->asBstr()->value()));
+ vector<uint8_t> payload_value;
+ check_maced_pubkey(key, testMode, &payload_value);
+ cborKeysToSign_.add(cppbor::EncodedItem(payload_value));
}
}
+ void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
+ const bytevec& keysToSignMac, const ProtectedData& protectedData) {
+ auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
+ ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
+ ASSERT_TRUE(parsedProtectedData->asArray());
+ ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+
+ auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
+ ASSERT_TRUE(senderPubkey) << senderPubkey.message();
+ EXPECT_EQ(senderPubkey->second, eekId_);
+
+ auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
+ senderPubkey->first, false /* senderIsA */);
+ ASSERT_TRUE(sessionKey) << sessionKey.message();
+
+ auto protectedDataPayload =
+ decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
+ ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
+
+ auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
+ ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
+ ASSERT_TRUE(parsedPayload->asArray());
+ EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
+
+ auto& signedMac = parsedPayload->asArray()->get(0);
+ auto& bcc = parsedPayload->asArray()->get(1);
+ ASSERT_TRUE(signedMac && signedMac->asArray());
+ ASSERT_TRUE(bcc && bcc->asArray());
+
+ // BCC is [ pubkey, + BccEntry]
+ auto bccContents = validateBcc(bcc->asArray());
+ ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
+ ASSERT_GT(bccContents->size(), 0U);
+
+ auto [deviceInfoMap, __2, deviceInfoErrMsg] = cppbor::parse(deviceInfo.deviceInfo);
+ ASSERT_TRUE(deviceInfoMap) << "Failed to parse deviceInfo: " << deviceInfoErrMsg;
+ ASSERT_TRUE(deviceInfoMap->asMap());
+
+ auto& signingKey = bccContents->back().pubKey;
+ auto macKey = verifyAndParseCoseSign1(/* ignore_signature = */ false, signedMac->asArray(),
+ signingKey,
+ cppbor::Array() // SignedMacAad
+ .add(challenge_)
+ .add(std::move(deviceInfoMap))
+ .encode());
+ ASSERT_TRUE(macKey) << macKey.message();
+
+ auto coseMac0 = cppbor::Array()
+ .add(cppbor::Map() // protected
+ .add(ALGORITHM, HMAC_256)
+ .canonicalize()
+ .encode())
+ .add(cppbor::Map()) // unprotected
+ .add(keysToSign.encode()) // payload (keysToSign)
+ .add(keysToSignMac); // tag
+
+ auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
+ ASSERT_TRUE(macPayload) << macPayload.message();
+ }
+
bytevec eekId_;
+ size_t eekLength_;
EekChain eekChain_;
+ bytevec challenge_;
std::vector<MacedPublicKey> keysToSign_;
cppbor::Array cborKeysToSign_;
};
@@ -226,65 +481,20 @@
*/
TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
bool testMode = true;
- bytevec keysToSignMac;
- ProtectedData protectedData;
- auto challenge = randomBytes(32);
- auto status = provisionable_->generateCertificateRequest(testMode, {} /* keysToSign */,
- eekChain_.chain, challenge,
- &keysToSignMac, &protectedData);
- ASSERT_TRUE(status.isOk()) << status.getMessage();
+ for (size_t eekLength : {2, 3, 7}) {
+ SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+ generateEek(eekLength);
- auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
- ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
- ASSERT_TRUE(parsedProtectedData->asArray());
- ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
- auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
- ASSERT_TRUE(senderPubkey) << senderPubkey.message();
- EXPECT_EQ(senderPubkey->second, eekId_);
-
- auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
- senderPubkey->first, false /* senderIsA */);
- ASSERT_TRUE(sessionKey) << sessionKey.message();
-
- auto protectedDataPayload =
- decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
- ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
-
- auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
- ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
- ASSERT_TRUE(parsedPayload->asArray());
- EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
-
- auto& signedMac = parsedPayload->asArray()->get(0);
- auto& bcc = parsedPayload->asArray()->get(1);
- ASSERT_TRUE(signedMac && signedMac->asArray());
- ASSERT_TRUE(bcc && bcc->asArray());
-
- // BCC is [ pubkey, + BccEntry]
- auto bccContents = validateBcc(bcc->asArray());
- ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
- ASSERT_GT(bccContents->size(), 0U);
-
- auto& signingKey = bccContents->back().pubKey;
- auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
- cppbor::Array() // DeviceInfo
- .add(challenge) //
- .add(cppbor::Map())
- .encode());
- ASSERT_TRUE(macKey) << macKey.message();
-
- auto coseMac0 = cppbor::Array()
- .add(cppbor::Map() // protected
- .add(ALGORITHM, HMAC_256)
- .canonicalize()
- .encode())
- .add(cppbor::Map()) // unprotected
- .add(cppbor::Array().encode()) // payload (keysToSign)
- .add(std::move(keysToSignMac)); // tag
-
- auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
- ASSERT_TRUE(macPayload) << macPayload.message();
+ checkProtectedData(deviceInfo, cppbor::Array(), keysToSignMac, protectedData);
+ }
}
/**
@@ -296,14 +506,20 @@
*/
TEST_P(CertificateRequestTest, EmptyRequest_prodMode) {
bool testMode = false;
- bytevec keysToSignMac;
- ProtectedData protectedData;
- auto challenge = randomBytes(32);
- auto status = provisionable_->generateCertificateRequest(testMode, {} /* keysToSign */,
- eekChain_.chain, challenge,
- &keysToSignMac, &protectedData);
- ASSERT_FALSE(status.isOk());
- ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ for (size_t eekLength : {2, 3, 7}) {
+ SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+ generateEek(eekLength);
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ }
}
/**
@@ -313,63 +529,20 @@
bool testMode = true;
generateKeys(testMode, 4 /* numKeys */);
- bytevec keysToSignMac;
- ProtectedData protectedData;
- auto challenge = randomBytes(32);
- auto status = provisionable_->generateCertificateRequest(
- testMode, keysToSign_, eekChain_.chain, challenge, &keysToSignMac, &protectedData);
- ASSERT_TRUE(status.isOk()) << status.getMessage();
+ for (size_t eekLength : {2, 3, 7}) {
+ SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+ generateEek(eekLength);
- auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
- ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
- ASSERT_TRUE(parsedProtectedData->asArray());
- ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
- auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
- ASSERT_TRUE(senderPubkey) << senderPubkey.message();
- EXPECT_EQ(senderPubkey->second, eekId_);
-
- auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
- senderPubkey->first, false /* senderIsA */);
- ASSERT_TRUE(sessionKey) << sessionKey.message();
-
- auto protectedDataPayload =
- decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
- ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
-
- auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
- ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
- ASSERT_TRUE(parsedPayload->asArray());
- EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
-
- auto& signedMac = parsedPayload->asArray()->get(0);
- auto& bcc = parsedPayload->asArray()->get(1);
- ASSERT_TRUE(signedMac && signedMac->asArray());
- ASSERT_TRUE(bcc);
-
- auto bccContents = validateBcc(bcc->asArray());
- ASSERT_TRUE(bccContents) << "\n" << prettyPrint(bcc.get());
- ASSERT_GT(bccContents->size(), 0U);
-
- auto& signingKey = bccContents->back().pubKey;
- auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
- cppbor::Array() // DeviceInfo
- .add(challenge) //
- .add(cppbor::Array())
- .encode());
- ASSERT_TRUE(macKey) << macKey.message();
-
- auto coseMac0 = cppbor::Array()
- .add(cppbor::Map() // protected
- .add(ALGORITHM, HMAC_256)
- .canonicalize()
- .encode())
- .add(cppbor::Map()) // unprotected
- .add(cborKeysToSign_.encode()) // payload
- .add(std::move(keysToSignMac)); // tag
-
- auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
- ASSERT_TRUE(macPayload) << macPayload.message();
+ checkProtectedData(deviceInfo, cborKeysToSign_, keysToSignMac, protectedData);
+ }
}
/**
@@ -383,11 +556,117 @@
bool testMode = false;
generateKeys(testMode, 4 /* numKeys */);
+ for (size_t eekLength : {2, 3, 7}) {
+ SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+ generateEek(eekLength);
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ }
+}
+
+/**
+ * Generate a non-empty certificate request in test mode, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) {
+ bool testMode = true;
+ generateKeys(testMode, 1 /* numKeys */);
+ MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue();
+
bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
ProtectedData protectedData;
- auto challenge = randomBytes(32);
auto status = provisionable_->generateCertificateRequest(
- testMode, keysToSign_, eekChain_.chain, challenge, &keysToSignMac, &protectedData);
+ testMode, {keyWithCorruptMac}, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_FALSE(status.isOk()) << status.getMessage();
+ EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) {
+ bool testMode = true;
+ generateKeys(testMode, 1 /* numKeys */);
+ MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue();
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, {keyWithCorruptMac}, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_FALSE(status.isOk()) << status.getMessage();
+ auto rc = status.getServiceSpecificError();
+
+ // TODO(drysdale): drop the INVALID_EEK potential error code when a real GEEK is available.
+ EXPECT_TRUE(rc == BnRemotelyProvisionedComponent::STATUS_INVALID_EEK ||
+ rc == BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode that has a corrupt EEK chain.
+ * Confirm that the request is rejected.
+ *
+ * TODO(drysdale): Update to use a valid GEEK, so that the test actually confirms that the
+ * implementation is checking signatures.
+ */
+TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) {
+ bool testMode = false;
+ generateKeys(testMode, 4 /* numKeys */);
+
+ for (size_t ii = 0; ii < eekLength_; ii++) {
+ auto chain = corrupt_sig_chain(eekChain_, ii);
+ ASSERT_TRUE(chain) << chain.message();
+ EekChain corruptEek = chain.moveValue();
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, corruptEek.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ }
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode that has an incomplete EEK chain.
+ * Confirm that the request is rejected.
+ *
+ * TODO(drysdale): Update to use a valid GEEK, so that the test actually confirms that the
+ * implementation is checking signatures.
+ */
+TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) {
+ bool testMode = false;
+ generateKeys(testMode, 4 /* numKeys */);
+
+ // Build an EEK chain that omits the first self-signed cert.
+ auto truncatedChain = cppbor::Array();
+ auto [chain, _, parseErr] = cppbor::parse(eekChain_.chain);
+ ASSERT_TRUE(chain);
+ auto eekChain = chain->asArray();
+ ASSERT_NE(eekChain, nullptr);
+ for (size_t ii = 1; ii < eekChain->size(); ii++) {
+ truncatedChain.add(eekChain->get(ii)->clone());
+ }
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, truncatedChain.encode(), challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
}
@@ -400,11 +679,11 @@
generateKeys(false /* testMode */, 2 /* numKeys */);
bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
ProtectedData protectedData;
- auto challenge = randomBytes(32);
- auto status = provisionable_->generateCertificateRequest(true /* testMode */, keysToSign_,
- eekChain_.chain, challenge,
- &keysToSignMac, &protectedData);
+ auto status = provisionable_->generateCertificateRequest(
+ true /* testMode */, keysToSign_, eekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(status.getServiceSpecificError(),
BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
@@ -418,10 +697,11 @@
generateKeys(true /* testMode */, 2 /* numKeys */);
bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
ProtectedData protectedData;
auto status = provisionable_->generateCertificateRequest(
- false /* testMode */, keysToSign_, eekChain_.chain, randomBytes(32) /* challenge */,
- &keysToSignMac, &protectedData);
+ false /* testMode */, keysToSign_, eekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(status.getServiceSpecificError(),
BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
diff --git a/security/keymint/support/include/keymint_support/openssl_utils.h b/security/keymint/support/include/keymint_support/openssl_utils.h
index a0212aa..dee28ba 100644
--- a/security/keymint/support/include/keymint_support/openssl_utils.h
+++ b/security/keymint/support/include/keymint_support/openssl_utils.h
@@ -37,6 +37,7 @@
MAKE_OPENSSL_PTR_TYPE(BN_CTX)
MAKE_OPENSSL_PTR_TYPE(EC_GROUP)
MAKE_OPENSSL_PTR_TYPE(EC_KEY)
+MAKE_OPENSSL_PTR_TYPE(EC_POINT)
MAKE_OPENSSL_PTR_TYPE(EVP_PKEY)
MAKE_OPENSSL_PTR_TYPE(EVP_PKEY_CTX)
MAKE_OPENSSL_PTR_TYPE(RSA)
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 3e4f3f7..da10eb2 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -54,6 +54,8 @@
{} /* AAD */);
if (!coseSign1) return coseSign1.moveMessage();
eekChain.add(coseSign1.moveValue());
+
+ prev_priv_key = priv_key;
}
bytevec pub_key(X25519_PUBLIC_VALUE_LEN);
diff --git a/security/secureclock/aidl/vts/functional/Android.bp b/security/secureclock/aidl/vts/functional/Android.bp
index 6dfa417..56c8e1d 100644
--- a/security/secureclock/aidl/vts/functional/Android.bp
+++ b/security/secureclock/aidl/vts/functional/Android.bp
@@ -39,11 +39,11 @@
shared_libs: [
"libbinder_ndk",
"libcrypto",
- "libkeymint",
],
static_libs: [
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.secureclock-V1-ndk_platform",
+ "libkeymint",
],
test_suites: [
"general-tests",
diff --git a/security/secureclock/aidl/vts/functional/AndroidTest.xml b/security/secureclock/aidl/vts/functional/AndroidTest.xml
deleted file mode 100644
index 4861c7c..0000000
--- a/security/secureclock/aidl/vts/functional/AndroidTest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Runs VtsAidlSecureClockTargetTest.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push"
- value="VtsAidlSecureClockTargetTest->/data/local/tmp/VtsAidlSecureClockTargetTest" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="VtsAidlSecureClockTargetTest" />
- <option name="native-test-timeout" value="900000"/>
- </test>
-</configuration>
diff --git a/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
index 9ca1ee8..31f4854 100644
--- a/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
+++ b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
@@ -185,9 +185,11 @@
INSTANTIATE_TEST_SUITE_P(PerInstance, SecureClockAidlTest,
testing::ValuesIn(SecureClockAidlTest::build_params()),
::android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureClockAidlTest);
+
} // namespace aidl::android::hardware::security::secureclock::test
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/security/sharedsecret/aidl/vts/functional/Android.bp b/security/sharedsecret/aidl/vts/functional/Android.bp
index 1bc5beb..d3747fc 100644
--- a/security/sharedsecret/aidl/vts/functional/Android.bp
+++ b/security/sharedsecret/aidl/vts/functional/Android.bp
@@ -39,11 +39,11 @@
shared_libs: [
"libbinder_ndk",
"libcrypto",
- "libkeymint",
],
static_libs: [
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.sharedsecret-V1-ndk_platform",
+ "libkeymint",
],
test_suites: [
"general-tests",
diff --git a/security/sharedsecret/aidl/vts/functional/AndroidTest.xml b/security/sharedsecret/aidl/vts/functional/AndroidTest.xml
deleted file mode 100644
index c6697bc..0000000
--- a/security/sharedsecret/aidl/vts/functional/AndroidTest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Runs VtsAidlSharedSecretTargetTest.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push"
- value="VtsAidlSharedSecretTargetTest->/data/local/tmp/VtsAidlSharedSecretTargetTest" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="VtsAidlSharedSecretTargetTest" />
- <option name="native-test-timeout" value="900000"/>
- </test>
-</configuration>
diff --git a/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
index 83f6ef3..8426120 100644
--- a/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
+++ b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
@@ -114,14 +114,14 @@
const vector<shared_ptr<ISharedSecret>>& allSharedSecrets() { return allSharedSecrets_; }
static void SetUpTestCase() {
- if (allSharedSecrets_.empty()) {
- auto names = ::android::getAidlHalInstanceNames(ISharedSecret::descriptor);
- for (const auto& name : names) {
- auto servicePtr = getSharedSecretService(name.c_str());
- if (servicePtr != nullptr) allSharedSecrets_.push_back(std::move(servicePtr));
- }
+ ASSERT_TRUE(allSharedSecrets_.empty()) << "The Shared Secret vector is not empty.";
+ auto names = ::android::getAidlHalInstanceNames(ISharedSecret::descriptor);
+ for (const auto& name : names) {
+ auto servicePtr = getSharedSecretService(name.c_str());
+ if (servicePtr != nullptr) allSharedSecrets_.push_back(std::move(servicePtr));
}
}
+
static void TearDownTestCase() {}
void SetUp() override {}
void TearDown() override {}
@@ -134,6 +134,9 @@
TEST_F(SharedSecretAidlTest, GetParameters) {
auto sharedSecrets = allSharedSecrets();
+ if (sharedSecrets.empty()) {
+ GTEST_SKIP() << "Skipping the test because no shared secret service is found.";
+ }
for (auto sharedSecret : sharedSecrets) {
auto result1 = getSharedSecretParameters(sharedSecret);
EXPECT_EQ(ErrorCode::OK, result1.error);
@@ -148,14 +151,18 @@
}
TEST_F(SharedSecretAidlTest, ComputeSharedSecret) {
+ auto sharedSecrets = allSharedSecrets();
+ if (sharedSecrets.empty()) {
+ GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
+ }
auto params = getAllSharedSecretParameters();
- ASSERT_EQ(allSharedSecrets().size(), params.size())
+ ASSERT_EQ(sharedSecrets.size(), params.size())
<< "One or more shared secret services failed to provide parameters.";
auto nonces = copyNonces(params);
- EXPECT_EQ(allSharedSecrets().size(), nonces.size());
+ EXPECT_EQ(sharedSecrets.size(), nonces.size());
std::sort(nonces.begin(), nonces.end());
std::unique(nonces.begin(), nonces.end());
- EXPECT_EQ(allSharedSecrets().size(), nonces.size());
+ EXPECT_EQ(sharedSecrets.size(), nonces.size());
auto responses = computeAllSharedSecrets(params);
ASSERT_GT(responses.size(), 0U);
@@ -163,7 +170,7 @@
// Do it a second time. Should get the same answers.
params = getAllSharedSecretParameters();
- ASSERT_EQ(allSharedSecrets().size(), params.size())
+ ASSERT_EQ(sharedSecrets.size(), params.size())
<< "One or more shared secret services failed to provide parameters.";
responses = computeAllSharedSecrets(params);
@@ -188,10 +195,14 @@
}
TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptNonce) {
+ auto sharedSecrets = allSharedSecrets();
+ if (sharedSecrets.empty()) {
+ GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
+ }
auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
auto params = getAllSharedSecretParameters();
- ASSERT_EQ(allSharedSecrets().size(), params.size())
+ ASSERT_EQ(sharedSecrets.size(), params.size())
<< "One or more shared secret services failed to provide parameters.";
// All should be well in the normal case
@@ -224,9 +235,13 @@
}
TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptSeed) {
+ auto sharedSecrets = allSharedSecrets();
+ if (sharedSecrets.empty()) {
+ GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
+ }
auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
auto params = getAllSharedSecretParameters();
- ASSERT_EQ(allSharedSecrets().size(), params.size())
+ ASSERT_EQ(sharedSecrets.size(), params.size())
<< "One or more shared secret service failed to provide parameters.";
// All should be well in the normal case
diff --git a/sensors/common/utils/EventMessageQueueWrapper.h b/sensors/common/utils/EventMessageQueueWrapper.h
index c4f92c8..63e4eb0 100644
--- a/sensors/common/utils/EventMessageQueueWrapper.h
+++ b/sensors/common/utils/EventMessageQueueWrapper.h
@@ -33,7 +33,7 @@
namespace V2_1 {
namespace implementation {
-class EventMessageQueueWrapperBase : public RefBase {
+class EventMessageQueueWrapperBase {
public:
virtual ~EventMessageQueueWrapperBase() {}
diff --git a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
index 8cf5003..47a8cc0 100644
--- a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
+++ b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
@@ -462,6 +462,7 @@
// Wait for events to be written back to the Event FMQ
callback.waitForEvents(sensors, milliseconds(1000) /* timeout */);
+ getEnvironment()->unregisterCallback();
for (const auto& s : sensors) {
auto events = callback.getEvents(s.sensorHandle);
@@ -485,7 +486,6 @@
ASSERT_EQ(lastEvent.u.vec3.status, injectedEvent.u.vec3.status);
}
- getEnvironment()->unregisterCallback();
ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
}
@@ -603,7 +603,7 @@
<< " type=" << static_cast<int>(sensor.type) << " name=" << sensor.name);
Result flushResult = flush(sensor.sensorHandle);
- ASSERT_EQ(flushResult, expectedResponse);
+ EXPECT_EQ(flushResult, expectedResponse);
}
}
diff --git a/tv/cec/1.0/vts/functional/Android.bp b/tv/cec/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..9a2c714
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalTvCecV1_0TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalTvCecV1_0TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.tv.cec@1.0",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ disable_framework: true,
+}
diff --git a/tv/cec/1.0/vts/functional/README.md b/tv/cec/1.0/vts/functional/README.md
new file mode 100644
index 0000000..aecd6a6
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/README.md
@@ -0,0 +1,30 @@
+# CEC VTS testing for Android TV devices
+
+Validate HDMI CEC VTS (android.hardware.tv.cec@1.0) functionality.
+
+### Setup:
+
+Running these CEC VTS tests requires an Android playback, TV or audio device connected to the host machine.
+
+
+
+### Building
+
+From the Android root folder, after choosing the lunch combo, use `make vts` to build VTS.
+
+### Automation
+
+On the host machine, ensure that the [software requirements](https://codelabs.developers.google.com/codelabs/android-lab/#2) for python SDK are met.
+
+Given the setup described above you can run tests with any of the following commands:
+
+1. Using vts-tradefed :
+```
+cd $ANDROID_BUILD_TOP/out/host/linux-x86/vts/android-vts/tools
+./vts-tradefed run commandAndExit vts -m VtsHalTvCecV1_0TargetTest
+```
+2. Using atest
+```
+atest VtsHalTvCecV1_0TargetTest
+```
+Note : atest internally handles building as well. To update the test use '-c' (clear cache) option
diff --git a/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp b/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp
new file mode 100644
index 0000000..7b42689
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HdmiCec_hal_test"
+#include <android-base/logging.h>
+
+#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
+#include <android/hardware/tv/cec/1.0/types.h>
+#include <utils/Log.h>
+#include <sstream>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::tv::cec::V1_0::CecDeviceType;
+using ::android::hardware::tv::cec::V1_0::CecLogicalAddress;
+using ::android::hardware::tv::cec::V1_0::CecMessage;
+using ::android::hardware::tv::cec::V1_0::HdmiPortInfo;
+using ::android::hardware::tv::cec::V1_0::HdmiPortType;
+using ::android::hardware::tv::cec::V1_0::IHdmiCec;
+using ::android::hardware::tv::cec::V1_0::OptionKey;
+using ::android::hardware::tv::cec::V1_0::Result;
+using ::android::hardware::tv::cec::V1_0::SendMessageResult;
+
+#define CEC_VERSION 0x05
+#define INCORRECT_VENDOR_ID 0x00
+#define TV_PHYSICAL_ADDRESS 0x0000
+
+// The main test class for TV CEC HAL.
+class HdmiCecTest : public ::testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ hdmiCec = IHdmiCec::getService(GetParam());
+ ASSERT_NE(hdmiCec, nullptr);
+ ALOGI("%s: getService() for hdmiCec is %s", __func__,
+ hdmiCec->isRemote() ? "remote" : "local");
+
+ hdmiCec_death_recipient = new HdmiCecDeathRecipient();
+ ASSERT_NE(hdmiCec_death_recipient, nullptr);
+ ASSERT_TRUE(hdmiCec->linkToDeath(hdmiCec_death_recipient, 0).isOk());
+ }
+
+ std::vector<int> getDeviceTypes() {
+ std::vector<int> deviceTypes;
+ FILE* p = popen("getprop ro.hdmi.device_type", "re");
+ if (p) {
+ char* line = NULL;
+ size_t len = 0;
+ if (getline(&line, &len, p) > 0) {
+ std::istringstream stream(line);
+ std::string number{};
+ while (std::getline(stream, number, ',')) {
+ deviceTypes.push_back(stoi(number));
+ }
+ }
+ pclose(p);
+ }
+ return deviceTypes;
+ }
+
+ bool hasDeviceType(CecDeviceType type) {
+ std::vector<int> deviceTypes = getDeviceTypes();
+ for (auto deviceType = deviceTypes.begin(); deviceType != deviceTypes.end(); ++deviceType) {
+ if (*deviceType == (int)type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ class HdmiCecDeathRecipient : public hidl_death_recipient {
+ public:
+ void serviceDied(uint64_t /*cookie*/,
+ const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
+ FAIL();
+ }
+ };
+
+ sp<IHdmiCec> hdmiCec;
+ sp<HdmiCecDeathRecipient> hdmiCec_death_recipient;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HdmiCecTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, HdmiCecTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHdmiCec::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+
+TEST_P(HdmiCecTest, ClearAddLogicalAddress) {
+ hdmiCec->clearLogicalAddress();
+ Return<Result> ret = hdmiCec->addLogicalAddress(CecLogicalAddress::PLAYBACK_3);
+ EXPECT_EQ(ret, Result::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, PhysicalAddress) {
+ Result result;
+ uint16_t addr;
+ Return<void> ret = hdmiCec->getPhysicalAddress([&result, &addr](Result res, uint16_t paddr) {
+ result = res;
+ addr = paddr;
+ });
+ EXPECT_TRUE(ret.isOk());
+ EXPECT_EQ(result, Result::SUCCESS);
+ if (!hasDeviceType(CecDeviceType::TV)) {
+ EXPECT_NE(addr, TV_PHYSICAL_ADDRESS);
+ }
+}
+
+TEST_P(HdmiCecTest, SendMessage) {
+ CecMessage message;
+ message.initiator = CecLogicalAddress::PLAYBACK_1;
+ message.destination = CecLogicalAddress::BROADCAST;
+ message.body.resize(1);
+ message.body[0] = 131;
+ SendMessageResult ret = hdmiCec->sendMessage(message);
+ EXPECT_EQ(ret, SendMessageResult::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, CecVersion) {
+ Return<int32_t> ret = hdmiCec->getCecVersion();
+ EXPECT_GE(ret, CEC_VERSION);
+}
+
+TEST_P(HdmiCecTest, VendorId) {
+ Return<uint32_t> ret = hdmiCec->getVendorId();
+ EXPECT_NE(ret, INCORRECT_VENDOR_ID);
+}
+
+TEST_P(HdmiCecTest, GetPortInfo) {
+ hidl_vec<HdmiPortInfo> ports;
+ Return<void> ret =
+ hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+ EXPECT_TRUE(ret.isOk());
+ bool cecSupportedOnDevice = false;
+ for (size_t i = 0; i < ports.size(); ++i) {
+ EXPECT_TRUE((ports[i].type == HdmiPortType::OUTPUT) ||
+ (ports[i].type == HdmiPortType::INPUT));
+ if (ports[i].portId == 0) {
+ ALOGW("%s: Port id should start from 1", __func__);
+ }
+ cecSupportedOnDevice = cecSupportedOnDevice | ports[i].cecSupported;
+ }
+ EXPECT_NE(cecSupportedOnDevice, false) << "At least one port should support CEC";
+}
+
+TEST_P(HdmiCecTest, SetOption) {
+ Return<void> ret;
+ ret = hdmiCec->setOption(OptionKey::WAKEUP, false);
+ EXPECT_TRUE(ret.isOk());
+ ret = hdmiCec->setOption(OptionKey::ENABLE_CEC, false);
+ EXPECT_TRUE(ret.isOk());
+ ret = hdmiCec->setOption(OptionKey::SYSTEM_CEC_CONTROL, true);
+ EXPECT_TRUE(ret.isOk());
+ // Restore option keys to their default values
+ ret = hdmiCec->setOption(OptionKey::WAKEUP, true);
+ EXPECT_TRUE(ret.isOk());
+ ret = hdmiCec->setOption(OptionKey::ENABLE_CEC, true);
+ EXPECT_TRUE(ret.isOk());
+ ret = hdmiCec->setOption(OptionKey::SYSTEM_CEC_CONTROL, false);
+ EXPECT_TRUE(ret.isOk());
+}
+
+TEST_P(HdmiCecTest, SetLanguage) {
+ Return<void> ret = hdmiCec->setLanguage("eng");
+ EXPECT_TRUE(ret.isOk());
+}
+
+TEST_P(HdmiCecTest, EnableAudioReturnChannel) {
+ hidl_vec<HdmiPortInfo> ports;
+ Return<void> ret =
+ hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+ EXPECT_TRUE(ret.isOk());
+ for (size_t i = 0; i < ports.size(); ++i) {
+ if (ports[i].arcSupported) {
+ ret = hdmiCec->enableAudioReturnChannel(ports[i].portId, true);
+ EXPECT_TRUE(ret.isOk());
+ }
+ }
+}
+
+TEST_P(HdmiCecTest, IsConnected) {
+ hidl_vec<HdmiPortInfo> ports;
+ Return<void> ret =
+ hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+ EXPECT_TRUE(ret.isOk());
+ for (size_t i = 0; i < ports.size(); ++i) {
+ Return<bool> ret = hdmiCec->isConnected(ports[i].portId);
+ EXPECT_TRUE(ret.isOk());
+ }
+}
diff --git a/tv/cec/1.0/vts/functional/setup.png b/tv/cec/1.0/vts/functional/setup.png
new file mode 100644
index 0000000..a64b86c
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/setup.png
Binary files differ
diff --git a/tv/cec/1.1/vts/functional/VtsHalTvCecV1_1TargetTest.cpp b/tv/cec/1.1/vts/functional/VtsHalTvCecV1_1TargetTest.cpp
index 1eb4643..c5b4b2f 100644
--- a/tv/cec/1.1/vts/functional/VtsHalTvCecV1_1TargetTest.cpp
+++ b/tv/cec/1.1/vts/functional/VtsHalTvCecV1_1TargetTest.cpp
@@ -46,6 +46,7 @@
#define CEC_VERSION 0x05
#define INCORRECT_VENDOR_ID 0x00
+#define TV_PHYSICAL_ADDRESS 0x0000
// The main test class for TV CEC HAL.
class HdmiCecTest : public ::testing::TestWithParam<std::string> {
@@ -126,6 +127,19 @@
EXPECT_EQ(ret, Result::SUCCESS);
}
+TEST_P(HdmiCecTest, PhysicalAddress) {
+ Result result;
+ uint16_t addr;
+ Return<void> ret = hdmiCec->getPhysicalAddress([&result, &addr](Result res, uint16_t paddr) {
+ result = res;
+ addr = paddr;
+ });
+ EXPECT_EQ(result, Result::SUCCESS);
+ if (!hasDeviceType(CecDeviceType::TV)) {
+ EXPECT_NE(addr, TV_PHYSICAL_ADDRESS);
+ }
+}
+
TEST_P(HdmiCecTest, SendMessage) {
CecMessage message;
message.initiator = CecLogicalAddress::PLAYBACK_1;
@@ -196,4 +210,14 @@
ASSERT_TRUE(ret.isOk());
}
}
-}
\ No newline at end of file
+}
+
+TEST_P(HdmiCecTest, IsConnected) {
+ hidl_vec<HdmiPortInfo> ports;
+ Return<void> ret =
+ hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+ for (size_t i = 0; i < ports.size(); ++i) {
+ Return<bool> ret = hdmiCec->isConnected(ports[i].portId);
+ EXPECT_TRUE(ret.isOk());
+ }
+}