Merge "ComposerClient 2.4: Clean cache on hotplug" into sc-dev
diff --git a/audio/7.0/IDevice.hal b/audio/7.0/IDevice.hal
index e423f29..85c789a 100644
--- a/audio/7.0/IDevice.hal
+++ b/audio/7.0/IDevice.hal
@@ -245,6 +245,7 @@
/**
* Gets the HW synchronization source of the device. Calling this method is
* equivalent to getting AUDIO_PARAMETER_HW_AV_SYNC on the legacy HAL.
+ *
* Optional method
*
* @return retval operation completion status: OK or NOT_SUPPORTED.
@@ -255,6 +256,7 @@
/**
* Sets whether the screen is on. Calling this method is equivalent to
* setting AUDIO_PARAMETER_KEY_SCREEN_STATE on the legacy HAL.
+ *
* Optional method
*
* @param turnedOn whether the screen is turned on.
diff --git a/audio/7.0/IStream.hal b/audio/7.0/IStream.hal
index 393e38f..e4987c2 100644
--- a/audio/7.0/IStream.hal
+++ b/audio/7.0/IStream.hal
@@ -110,6 +110,7 @@
/**
* Return the set of devices which this stream is connected to.
+ *
* Optional method
*
* @return retval operation completion status: OK or NOT_SUPPORTED.
@@ -133,6 +134,7 @@
/**
* Sets the HW synchronization source. Calling this method is equivalent to
* setting AUDIO_PARAMETER_STREAM_HW_AV_SYNC on the legacy HAL.
+ *
* Optional method
*
* @param hwAvSync HW synchronization source
diff --git a/audio/7.0/IStreamIn.hal b/audio/7.0/IStreamIn.hal
index bf9ae52..be4bda4 100644
--- a/audio/7.0/IStreamIn.hal
+++ b/audio/7.0/IStreamIn.hal
@@ -24,6 +24,7 @@
* Returns the source descriptor of the input stream. Calling this method is
* equivalent to getting AUDIO_PARAMETER_STREAM_INPUT_SOURCE on the legacy
* HAL.
+ *
* Optional method
*
* @return retval operation completion status.
@@ -33,6 +34,7 @@
/**
* Set the input gain for the audio driver.
+ *
* Optional method
*
* @param gain 1.0f is unity, 0.0f is zero.
@@ -42,6 +44,7 @@
/**
* Called when the metadata of the stream's sink has been changed.
+ *
* Optional method
*
* @param sinkMetadata Description of the audio that is suggested by the clients.
@@ -148,7 +151,8 @@
/**
* Return a recent count of the number of audio frames received and the
- * clock time associated with that frame count.
+ * clock time associated with that frame count. The count must not reset to
+ * zero when a PCM input enters standby.
*
* @return retval INVALID_STATE if the device is not ready/available,
* NOT_SUPPORTED if the command is not supported,
diff --git a/audio/7.0/IStreamOut.hal b/audio/7.0/IStreamOut.hal
index 78cb51b..6e8498e 100644
--- a/audio/7.0/IStreamOut.hal
+++ b/audio/7.0/IStreamOut.hal
@@ -35,6 +35,7 @@
* allowing to directly set the volume as apposed to via the framework.
* This method might produce multiple PCM outputs or hardware accelerated
* codecs, such as MP3 or AAC.
+ *
* Optional method
*
* @param left left channel attenuation, 1.0f is unity, 0.0f is zero.
@@ -46,6 +47,7 @@
/**
* Called when the metadata of the stream's source has been changed.
+ *
* Optional method
*
* @param sourceMetadata Description of the audio that is played by the clients.
@@ -130,6 +132,7 @@
/**
* Return the number of audio frames written by the audio DSP to DAC since
* the output has exited standby.
+ *
* Optional method
*
* @return retval operation completion status.
@@ -141,6 +144,7 @@
* Get the local time at which the next write to the audio driver will be
* presented. The units are microseconds, where the epoch is decided by the
* local audio HAL.
+ *
* Optional method
*
* @return retval operation completion status.
@@ -253,8 +257,11 @@
drain(AudioDrain type) generates (Result retval);
/**
- * Notifies to the audio driver to flush the queued data. Stream must
- * already be paused before calling 'flush'.
+ * Notifies to the audio driver to flush (that is, drop) the queued
+ * data. Stream must already be paused before calling 'flush'. For
+ * compressed and offload streams the frame count returned by
+ * 'getPresentationPosition' must reset after flush.
+ *
* Optional method
*
* Implementation of this function is mandatory for offloaded playback.
@@ -266,12 +273,14 @@
/**
* Return a recent count of the number of audio frames presented to an
* external observer. This excludes frames which have been written but are
- * still in the pipeline. The count is not reset to zero when output enters
- * standby. Also returns the value of CLOCK_MONOTONIC as of this
+ * still in the pipeline. The count must not reset to zero when a PCM output
+ * enters standby. For compressed and offload streams it is recommended that
+ * HAL resets the frame count.
+ *
+ * This method also returns the value of CLOCK_MONOTONIC as of this
* presentation count. The returned count is expected to be 'recent', but
* does not need to be the most recent possible value. However, the
* associated time must correspond to whatever count is returned.
- *
* Example: assume that N+M frames have been presented, where M is a 'small'
* number. Then it is permissible to return N instead of N+M, and the
* timestamp must correspond to N rather than N+M. The terms 'recent' and
@@ -287,6 +296,7 @@
/**
* Selects a presentation for decoding from a next generation media stream
* (as defined per ETSI TS 103 190-2) and a program within the presentation.
+ *
* Optional method
*
* @param presentationId selected audio presentation.
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/effect/all-versions/default/OWNERS b/audio/common/7.0/enums/OWNERS
similarity index 67%
rename from audio/effect/all-versions/default/OWNERS
rename to audio/common/7.0/enums/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/effect/all-versions/default/OWNERS
+++ b/audio/common/7.0/enums/OWNERS
@@ -1,3 +1,2 @@
elaurent@google.com
-krocard@google.com
mnaganov@google.com
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 fe8eee1..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
@@ -212,6 +212,15 @@
return isOutputDevice(stringToAudioDevice(device));
}
+static inline bool isTelephonyDevice(AudioDevice device) {
+ return device == AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX ||
+ device == AudioDevice::AUDIO_DEVICE_IN_TELEPHONY_RX;
+}
+
+static inline bool isTelephonyDevice(const std::string& device) {
+ return isTelephonyDevice(stringToAudioDevice(device));
+}
+
static inline bool maybeVendorExtension(const std::string& s) {
// Only checks whether the string starts with the "vendor prefix".
static const std::string vendorPrefix = "VX_";
@@ -260,6 +269,28 @@
return stringToAudioUsage(usage) == AudioUsage::UNKNOWN;
}
+static inline bool isLinearPcm(AudioFormat format) {
+ switch (format) {
+ case AudioFormat::AUDIO_FORMAT_PCM_16_BIT:
+ case AudioFormat::AUDIO_FORMAT_PCM_8_BIT:
+ case AudioFormat::AUDIO_FORMAT_PCM_32_BIT:
+ case AudioFormat::AUDIO_FORMAT_PCM_8_24_BIT:
+ case AudioFormat::AUDIO_FORMAT_PCM_FLOAT:
+ case AudioFormat::AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool isLinearPcm(const std::string& format) {
+ 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/effect/all-versions/default/OWNERS b/audio/common/7.0/example/OWNERS
similarity index 67%
copy from audio/effect/all-versions/default/OWNERS
copy to audio/common/7.0/example/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/effect/all-versions/default/OWNERS
+++ b/audio/common/7.0/example/OWNERS
@@ -1,3 +1,2 @@
elaurent@google.com
-krocard@google.com
mnaganov@google.com
diff --git a/audio/common/7.0/types.hal b/audio/common/7.0/types.hal
index bea0705..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.
*/
@@ -344,7 +381,7 @@
DeviceAddress device;
} destination;
AudioChannelMask channelMask;
- /** Tags from AudioTrack audio atttributes */
+ /** Tags from AudioRecord audio atttributes */
vec<AudioTag> tags;
};
@@ -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/OWNERS b/audio/common/all-versions/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/common/all-versions/OWNERS
+++ b/audio/common/all-versions/OWNERS
@@ -1,3 +1,2 @@
elaurent@google.com
-krocard@google.com
mnaganov@google.com
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/OWNERS b/audio/common/all-versions/default/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/common/all-versions/default/OWNERS
+++ b/audio/common/all-versions/default/OWNERS
@@ -1,3 +1,2 @@
elaurent@google.com
-krocard@google.com
mnaganov@google.com
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/common/all-versions/test/OWNERS b/audio/common/all-versions/test/OWNERS
deleted file mode 100644
index 6a26ae7..0000000
--- a/audio/common/all-versions/test/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-yim@google.com
-zhuoyao@google.com
diff --git a/audio/core/all-versions/default/OWNERS b/audio/core/all-versions/OWNERS
similarity index 100%
rename from audio/core/all-versions/default/OWNERS
rename to audio/core/all-versions/OWNERS
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/OWNERS b/audio/core/all-versions/vts/OWNERS
deleted file mode 100644
index 0ea4666..0000000
--- a/audio/core/all-versions/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-elaurent@google.com
-krocard@google.com
-mnaganov@google.com
-yim@google.com
-zhuoyao@google.com
diff --git a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
index f87e5ed..b96cc83 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -77,7 +77,6 @@
.tags = {},
.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
#endif
- EventFlag* efGroup;
for (auto microphone : microphones) {
#if MAJOR_VERSION <= 6
if (microphone.deviceAddress.device != AudioDevice::IN_BUILTIN_MIC) {
@@ -96,44 +95,15 @@
config, flags, initMetadata, cb);
},
config, &res, &suggestedConfig));
+ StreamReader reader(stream.get(), stream->getBufferSize());
+ ASSERT_TRUE(reader.start());
+ reader.pause(); // This ensures that at least one read has happened.
+ EXPECT_FALSE(reader.hasError());
+
hidl_vec<MicrophoneInfo> activeMicrophones;
- Result readRes;
- typedef MessageQueue<IStreamIn::ReadParameters, kSynchronizedReadWrite> CommandMQ;
- typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
- std::unique_ptr<CommandMQ> commandMQ;
- std::unique_ptr<DataMQ> dataMQ;
- size_t frameSize = stream->getFrameSize();
- size_t frameCount = stream->getBufferSize() / frameSize;
- ASSERT_OK(stream->prepareForReading(
- frameSize, frameCount, [&](auto r, auto& c, auto& d, auto&, auto) {
- readRes = r;
- if (readRes == Result::OK) {
- commandMQ.reset(new CommandMQ(c));
- dataMQ.reset(new DataMQ(d));
- if (dataMQ->isValid() && dataMQ->getEventFlagWord()) {
- EventFlag::createEventFlag(dataMQ->getEventFlagWord(), &efGroup);
- }
- }
- }));
- ASSERT_OK(readRes);
- IStreamIn::ReadParameters params;
- params.command = IStreamIn::ReadCommand::READ;
- ASSERT_TRUE(commandMQ != nullptr);
- ASSERT_TRUE(commandMQ->isValid());
- ASSERT_TRUE(commandMQ->write(¶ms));
- efGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
- uint32_t efState = 0;
- efGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState);
- if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)) {
- ASSERT_OK(stream->getActiveMicrophones(returnIn(res, activeMicrophones)));
- ASSERT_OK(res);
- ASSERT_NE(0U, activeMicrophones.size());
- }
- helper.close(true /*clear*/, &res);
+ ASSERT_OK(stream->getActiveMicrophones(returnIn(res, activeMicrophones)));
ASSERT_OK(res);
- if (efGroup) {
- EventFlag::deleteEventFlag(&efGroup);
- }
+ EXPECT_NE(0U, activeMicrophones.size());
}
}
}
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 c1923f1..0b3098b 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <android-base/chrono_utils.h>
+
#include "Generators.h"
// pull in all the <= 6.0 tests
@@ -487,3 +489,353 @@
<< ::testing::PrintToString(metadata);
}
}
+
+static const std::vector<DeviceConfigParameter>& getOutputDevicePcmOnlyConfigParameters() {
+ static const std::vector<DeviceConfigParameter> parameters = [] {
+ auto allParams = getOutputDeviceConfigParameters();
+ std::vector<DeviceConfigParameter> pcmParams;
+ std::copy_if(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 and HW A/V Sync profiles use special writing protocols.
+ &&
+ std::find_if(flags.begin(), flags.end(),
+ [](const auto& flag) {
+ return flag == toString(xsd::AudioInOutFlag::
+ AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) ||
+ flag == toString(xsd::AudioInOutFlag::
+ AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
+ }) == flags.end() &&
+ !getCachedPolicyConfig()
+ .getAttachedSinkDeviceForMixPort(
+ std::get<PARAM_DEVICE_NAME>(std::get<PARAM_DEVICE>(cfg)),
+ std::get<PARAM_PORT_NAME>(cfg))
+ .empty();
+ });
+ return pcmParams;
+ }();
+ return parameters;
+}
+
+class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
+ public:
+ void TearDown() override {
+ releasePatchIfNeeded();
+ OutputStreamTest::TearDown();
+ }
+
+ bool canQueryPresentationPosition() const {
+ auto maybeSinkAddress =
+ getCachedPolicyConfig().getSinkDeviceForMixPort(getDeviceName(), getMixPortName());
+ // Returning 'true' when no sink is found so the test can fail later with a more clear
+ // problem description.
+ return !maybeSinkAddress.has_value() ||
+ !xsd::isTelephonyDevice(maybeSinkAddress.value().deviceType);
+ }
+
+ void createPatchIfNeeded() {
+ auto maybeSinkAddress =
+ getCachedPolicyConfig().getSinkDeviceForMixPort(getDeviceName(), getMixPortName());
+ ASSERT_TRUE(maybeSinkAddress.has_value())
+ << "No sink device found for mix port " << getMixPortName() << " (module "
+ << getDeviceName() << ")";
+ if (areAudioPatchesSupported()) {
+ AudioPortConfig source;
+ source.base.format.value(getConfig().base.format);
+ source.base.sampleRateHz.value(getConfig().base.sampleRateHz);
+ source.base.channelMask.value(getConfig().base.channelMask);
+ source.ext.mix({});
+ source.ext.mix().ioHandle = helper.getIoHandle();
+ source.ext.mix().useCase.stream({});
+ AudioPortConfig sink;
+ sink.ext.device(maybeSinkAddress.value());
+ EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{source},
+ hidl_vec<AudioPortConfig>{sink},
+ returnIn(res, mPatchHandle)));
+ mHasPatch = res == Result::OK;
+ } else {
+ EXPECT_OK(stream->setDevices({maybeSinkAddress.value()}));
+ }
+ }
+
+ void releasePatchIfNeeded() {
+ if (areAudioPatchesSupported()) {
+ if (mHasPatch) {
+ EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
+ mHasPatch = false;
+ }
+ } else {
+ EXPECT_OK(stream->setDevices({address}));
+ }
+ }
+
+ const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+
+ void waitForPresentationPositionAdvance(StreamWriter& writer, uint64_t* firstPosition = nullptr,
+ uint64_t* lastPosition = nullptr) {
+ static constexpr int kWriteDurationUs = 50 * 1000;
+ static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
+ uint64_t framesInitial;
+ TimeSpec ts;
+ // 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.
+ 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);
+ }
+ ASSERT_FALSE(writer.hasError());
+ ASSERT_FALSE(timedOut);
+
+ uint64_t frames = framesInitial;
+ for (android::base::Timer elapsed;
+ frames <= framesInitial && !writer.hasError() &&
+ !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+ usleep(kWriteDurationUs);
+ ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, ts)));
+ ASSERT_RESULT(Result::OK, res);
+ }
+ EXPECT_FALSE(timedOut);
+ EXPECT_FALSE(writer.hasError());
+ EXPECT_GT(frames, framesInitial);
+ if (firstPosition) *firstPosition = framesInitial;
+ if (lastPosition) *lastPosition = frames;
+ }
+
+ private:
+ AudioPatchHandle mPatchHandle = {};
+ bool mHasPatch = false;
+};
+
+TEST_P(PcmOnlyConfigOutputStreamTest, Write) {
+ doc::test("Check that output streams opened for PCM output accepts audio data");
+ StreamWriter writer(stream.get(), stream->getBufferSize());
+ ASSERT_TRUE(writer.start());
+ EXPECT_TRUE(writer.waitForAtLeastOneCycle());
+}
+
+TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionAdvancesWithWrites) {
+ doc::test("Check that the presentation position advances with writes");
+ if (!canQueryPresentationPosition()) {
+ GTEST_SKIP() << "Presentation position retrieval is not possible";
+ }
+
+ ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
+ StreamWriter writer(stream.get(), stream->getBufferSize());
+ ASSERT_TRUE(writer.start());
+ ASSERT_TRUE(writer.waitForAtLeastOneCycle());
+ ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer));
+
+ writer.stop();
+ releasePatchIfNeeded();
+}
+
+TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionPreservedOnStandby) {
+ doc::test("Check that the presentation position does not reset on standby");
+ if (!canQueryPresentationPosition()) {
+ GTEST_SKIP() << "Presentation position retrieval is not possible";
+ }
+
+ ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
+ StreamWriter writer(stream.get(), stream->getBufferSize());
+ ASSERT_TRUE(writer.start());
+ ASSERT_TRUE(writer.waitForAtLeastOneCycle());
+
+ uint64_t framesInitial;
+ ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, nullptr, &framesInitial));
+ writer.pause();
+ ASSERT_OK(stream->standby());
+ writer.resume();
+
+ uint64_t frames;
+ ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, &frames));
+ EXPECT_GT(frames, framesInitial);
+
+ writer.stop();
+ releasePatchIfNeeded();
+}
+
+INSTANTIATE_TEST_CASE_P(PcmOnlyConfigOutputStream, PcmOnlyConfigOutputStreamTest,
+ ::testing::ValuesIn(getOutputDevicePcmOnlyConfigParameters()),
+ &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PcmOnlyConfigOutputStreamTest);
+
+static const std::vector<DeviceConfigParameter>& getInputDevicePcmOnlyConfigParameters() {
+ static const std::vector<DeviceConfigParameter> parameters = [] {
+ auto allParams = getInputDeviceConfigParameters();
+ std::vector<DeviceConfigParameter> pcmParams;
+ std::copy_if(
+ 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,
+ // reading h/w hotword might require Soundtrigger to be active.
+ &&
+ 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>(
+ std::get<PARAM_DEVICE>(cfg)),
+ std::get<PARAM_PORT_NAME>(cfg))
+ .empty();
+ });
+ return pcmParams;
+ }();
+ return parameters;
+}
+
+class PcmOnlyConfigInputStreamTest : public InputStreamTest {
+ public:
+ void TearDown() override {
+ releasePatchIfNeeded();
+ 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());
+ ASSERT_TRUE(maybeSourceAddress.has_value())
+ << "No source device found for mix port " << getMixPortName() << " (module "
+ << getDeviceName() << ")";
+ if (areAudioPatchesSupported()) {
+ AudioPortConfig source;
+ source.ext.device(maybeSourceAddress.value());
+ AudioPortConfig sink;
+ sink.base.format.value(getConfig().base.format);
+ sink.base.sampleRateHz.value(getConfig().base.sampleRateHz);
+ sink.base.channelMask.value(getConfig().base.channelMask);
+ sink.ext.mix({});
+ sink.ext.mix().ioHandle = helper.getIoHandle();
+ sink.ext.mix().useCase.source(toString(xsd::AudioSource::AUDIO_SOURCE_MIC));
+ EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{source},
+ hidl_vec<AudioPortConfig>{sink},
+ returnIn(res, mPatchHandle)));
+ mHasPatch = res == Result::OK;
+ } else {
+ EXPECT_OK(stream->setDevices({maybeSourceAddress.value()}));
+ }
+ }
+
+ void releasePatchIfNeeded() {
+ if (areAudioPatchesSupported()) {
+ if (mHasPatch) {
+ EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
+ mHasPatch = false;
+ }
+ } else {
+ EXPECT_OK(stream->setDevices({address}));
+ }
+ }
+
+ 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 = {};
+ bool mHasPatch = false;
+};
+
+TEST_P(PcmOnlyConfigInputStreamTest, Read) {
+ doc::test("Check that input streams opened for PCM input retrieve audio data");
+ StreamReader reader(stream.get(), stream->getBufferSize());
+ ASSERT_TRUE(reader.start());
+ EXPECT_TRUE(reader.waitForAtLeastOneCycle());
+}
+
+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());
+ 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;
+ ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, nullptr, &framesInitial));
+ reader.pause();
+ ASSERT_OK(stream->standby());
+ reader.resume();
+
+ uint64_t frames;
+ ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, &frames, nullptr));
+ EXPECT_GT(frames, framesInitial);
+
+ reader.stop();
+ releasePatchIfNeeded();
+}
+
+INSTANTIATE_TEST_CASE_P(PcmOnlyConfigInputStream, PcmOnlyConfigInputStreamTest,
+ ::testing::ValuesIn(getInputDevicePcmOnlyConfigParameters()),
+ &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PcmOnlyConfigInputStreamTest);
diff --git a/audio/core/all-versions/vts/functional/7.0/Generators.cpp b/audio/core/all-versions/vts/functional/7.0/Generators.cpp
index eafc813..d2ba339 100644
--- a/audio/core/all-versions/vts/functional/7.0/Generators.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/Generators.cpp
@@ -110,7 +110,7 @@
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(config.base));
}
- result.emplace_back(device, config, flags);
+ result.emplace_back(device, mixPort.getName(), config, flags);
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
@@ -160,7 +160,7 @@
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(validBase));
}
- result.emplace_back(device, config, validFlags);
+ result.emplace_back(device, mixPort.getName(), config, validFlags);
}
{
AudioConfig config{.base = validBase};
@@ -168,7 +168,7 @@
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(validBase));
}
- result.emplace_back(device, config, validFlags);
+ result.emplace_back(device, mixPort.getName(), config, validFlags);
}
if (generateInvalidFlags) {
AudioConfig config{.base = validBase};
@@ -176,32 +176,32 @@
config.offloadInfo.info(generateOffloadInfo(validBase));
}
std::vector<AudioInOutFlag> flags = {"random_string", ""};
- result.emplace_back(device, config, flags);
+ result.emplace_back(device, mixPort.getName(), config, flags);
}
if (isOffload) {
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().base.channelMask = "random_string";
- result.emplace_back(device, config, validFlags);
+ result.emplace_back(device, mixPort.getName(), config, validFlags);
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().base.format = "random_string";
- result.emplace_back(device, config, validFlags);
+ result.emplace_back(device, mixPort.getName(), config, validFlags);
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().streamType = "random_string";
- result.emplace_back(device, config, validFlags);
+ result.emplace_back(device, mixPort.getName(), config, validFlags);
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().usage = "random_string";
- result.emplace_back(device, config, validFlags);
+ result.emplace_back(device, mixPort.getName(), config, validFlags);
}
hasOffloadConfig = true;
} else {
@@ -234,7 +234,7 @@
auto configs = combineAudioConfig(profile.getChannelMasks(),
profile.getSamplingRates(), profile.getFormat());
for (const auto& config : configs) {
- result.emplace_back(device, config, flags);
+ result.emplace_back(device, mixPort.getName(), config, flags);
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
@@ -285,17 +285,17 @@
{
AudioConfig config{.base = validBase};
config.base.channelMask = "random_string";
- result.emplace_back(device, config, validFlags);
+ result.emplace_back(device, mixPort.getName(), config, validFlags);
}
{
AudioConfig config{.base = validBase};
config.base.format = "random_string";
- result.emplace_back(device, config, validFlags);
+ result.emplace_back(device, mixPort.getName(), config, validFlags);
}
if (generateInvalidFlags) {
AudioConfig config{.base = validBase};
std::vector<AudioInOutFlag> flags = {"random_string", ""};
- result.emplace_back(device, config, flags);
+ result.emplace_back(device, mixPort.getName(), config, flags);
}
hasConfig = true;
break;
diff --git a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.cpp b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.cpp
new file mode 100644
index 0000000..2988207
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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 <fcntl.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <HidlUtils.h>
+#include <system/audio.h>
+#include <system/audio_config.h>
+
+#include "DeviceManager.h"
+#include "PolicyConfig.h"
+#include "common/all-versions/HidlSupport.h"
+
+using ::android::NO_ERROR;
+using ::android::OK;
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::utils::splitString;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
+using Module = Modules::Module;
+} // namespace xsd
+
+std::string PolicyConfig::getError() const {
+ if (mFilePath.empty()) {
+ return "Could not find " + mConfigFileName +
+ " file in: " + testing::PrintToString(android::audio_get_configuration_paths());
+ } else {
+ return "Invalid config file: " + mFilePath;
+ }
+}
+
+const xsd::Module* PolicyConfig::getModuleFromName(const std::string& name) const {
+ if (mConfig && mConfig->getFirstModules()) {
+ for (const auto& module : mConfig->getFirstModules()->get_module()) {
+ if (module.getName() == name) return &module;
+ }
+ }
+ return nullptr;
+}
+
+std::optional<DeviceAddress> PolicyConfig::getSinkDeviceForMixPort(
+ const std::string& moduleName, const std::string& mixPortName) const {
+ std::string device;
+ if (auto module = getModuleFromName(moduleName); module) {
+ auto possibleDevices = getSinkDevicesForMixPort(moduleName, mixPortName);
+ if (module->hasDefaultOutputDevice() &&
+ possibleDevices.count(module->getDefaultOutputDevice())) {
+ device = module->getDefaultOutputDevice();
+ } else {
+ device = getAttachedSinkDeviceForMixPort(moduleName, mixPortName);
+ }
+ }
+ if (!device.empty()) {
+ return getDeviceAddressOfDevicePort(moduleName, device);
+ }
+ ALOGE("Could not find a route for the mix port \"%s\" in module \"%s\"", mixPortName.c_str(),
+ moduleName.c_str());
+ return std::optional<DeviceAddress>{};
+}
+
+std::optional<DeviceAddress> PolicyConfig::getSourceDeviceForMixPort(
+ const std::string& moduleName, const std::string& mixPortName) const {
+ const std::string device = getAttachedSourceDeviceForMixPort(moduleName, mixPortName);
+ if (!device.empty()) {
+ return getDeviceAddressOfDevicePort(moduleName, device);
+ }
+ ALOGE("Could not find a route for the mix port \"%s\" in module \"%s\"", mixPortName.c_str(),
+ moduleName.c_str());
+ return std::optional<DeviceAddress>{};
+}
+
+bool PolicyConfig::haveInputProfilesInModule(const std::string& name) const {
+ auto module = getModuleFromName(name);
+ if (module && module->getFirstMixPorts()) {
+ for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+ if (mixPort.getRole() == xsd::Role::sink) return true;
+ }
+ }
+ return false;
+}
+
+// static
+std::string PolicyConfig::findExistingConfigurationFile(const std::string& fileName) {
+ for (const auto& location : android::audio_get_configuration_paths()) {
+ std::string path = location + '/' + fileName;
+ if (access(path.c_str(), F_OK) == 0) {
+ return path;
+ }
+ }
+ return {};
+}
+
+std::string PolicyConfig::findAttachedDevice(const std::vector<std::string>& attachedDevices,
+ const std::set<std::string>& possibleDevices) const {
+ for (const auto& device : attachedDevices) {
+ if (possibleDevices.count(device)) return device;
+ }
+ return {};
+}
+
+const std::vector<std::string>& PolicyConfig::getAttachedDevices(
+ const std::string& moduleName) const {
+ static const std::vector<std::string> empty;
+ auto module = getModuleFromName(moduleName);
+ if (module && module->getFirstAttachedDevices()) {
+ return module->getFirstAttachedDevices()->getItem();
+ }
+ return empty;
+}
+
+std::optional<DeviceAddress> PolicyConfig::getDeviceAddressOfDevicePort(
+ const std::string& moduleName, const std::string& devicePortName) const {
+ auto module = getModuleFromName(moduleName);
+ if (module->getFirstDevicePorts()) {
+ const auto& devicePorts = module->getFirstDevicePorts()->getDevicePort();
+ const auto& devicePort = std::find_if(
+ devicePorts.begin(), devicePorts.end(),
+ [&devicePortName](auto dp) { return dp.getTagName() == devicePortName; });
+ if (devicePort != devicePorts.end()) {
+ audio_devices_t halDeviceType;
+ if (HidlUtils::audioDeviceTypeToHal(devicePort->getType(), &halDeviceType) ==
+ NO_ERROR) {
+ // For AOSP device types use the standard parser for the device address.
+ const std::string address =
+ devicePort->hasAddress() ? devicePort->getAddress() : "";
+ DeviceAddress result;
+ if (HidlUtils::deviceAddressFromHal(halDeviceType, address.c_str(), &result) ==
+ NO_ERROR) {
+ return result;
+ }
+ } else if (xsd::isVendorExtension(devicePort->getType())) {
+ DeviceAddress result;
+ result.deviceType = devicePort->getType();
+ if (devicePort->hasAddress()) {
+ result.address.id(devicePort->getAddress());
+ }
+ return result;
+ }
+ } else {
+ ALOGE("Device port \"%s\" not found in module \"%s\"", devicePortName.c_str(),
+ moduleName.c_str());
+ }
+ } else {
+ ALOGE("Module \"%s\" has no device ports", moduleName.c_str());
+ }
+ return std::optional<DeviceAddress>{};
+}
+
+std::set<std::string> PolicyConfig::getSinkDevicesForMixPort(const std::string& moduleName,
+ const std::string& mixPortName) const {
+ std::set<std::string> result;
+ auto module = getModuleFromName(moduleName);
+ if (module && module->getFirstRoutes()) {
+ for (const auto& route : module->getFirstRoutes()->getRoute()) {
+ const auto sources = splitString(route.getSources(), ',');
+ if (std::find(sources.begin(), sources.end(), mixPortName) != sources.end()) {
+ result.insert(route.getSink());
+ }
+ }
+ }
+ return result;
+}
+
+std::set<std::string> PolicyConfig::getSourceDevicesForMixPort(
+ const std::string& moduleName, const std::string& mixPortName) const {
+ std::set<std::string> result;
+ auto module = getModuleFromName(moduleName);
+ if (module && module->getFirstRoutes()) {
+ const auto& routes = module->getFirstRoutes()->getRoute();
+ const auto route = std::find_if(routes.begin(), routes.end(), [&mixPortName](auto rte) {
+ return rte.getSink() == mixPortName;
+ });
+ if (route != routes.end()) {
+ const auto sources = splitString(route->getSources(), ',');
+ std::copy(sources.begin(), sources.end(), std::inserter(result, result.end()));
+ }
+ }
+ return result;
+}
+
+void PolicyConfig::init() {
+ if (mConfig) {
+ mStatus = OK;
+ mPrimaryModule = getModuleFromName(DeviceManager::kPrimaryDevice);
+ if (mConfig->getFirstModules()) {
+ for (const auto& module : mConfig->getFirstModules()->get_module()) {
+ if (module.getFirstAttachedDevices()) {
+ auto attachedDevices = module.getFirstAttachedDevices()->getItem();
+ if (!attachedDevices.empty()) {
+ mModulesWithDevicesNames.insert(module.getName());
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h
index feb4d4b..f798839 100644
--- a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h
+++ b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h
@@ -16,15 +16,12 @@
#pragma once
-#include <fcntl.h>
-#include <unistd.h>
-
#include <optional>
#include <set>
#include <string>
+#include <vector>
#include <gtest/gtest.h>
-#include <system/audio_config.h>
#include <utils/Errors.h>
// clang-format off
@@ -35,12 +32,6 @@
#include <android_audio_policy_configuration_V7_0-enums.h>
#include <android_audio_policy_configuration_V7_0.h>
-#include "DeviceManager.h"
-
-using ::android::NO_INIT;
-using ::android::OK;
-using ::android::status_t;
-
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::CPP_VERSION;
namespace xsd {
@@ -62,69 +53,49 @@
mConfig{xsd::read(mFilePath.c_str())} {
init();
}
- status_t getStatus() const { return mStatus; }
- std::string getError() const {
- if (mFilePath.empty()) {
- return std::string{"Could not find "} + mConfigFileName +
- " file in: " + testing::PrintToString(android::audio_get_configuration_paths());
- } else {
- return "Invalid config file: " + mFilePath;
- }
- }
+ android::status_t getStatus() const { return mStatus; }
+ std::string getError() const;
const std::string& getFilePath() const { return mFilePath; }
- const xsd::Module* getModuleFromName(const std::string& name) const {
- if (mConfig && mConfig->getFirstModules()) {
- for (const auto& module : mConfig->getFirstModules()->get_module()) {
- if (module.getName() == name) return &module;
- }
- }
- return nullptr;
- }
+ const xsd::Module* getModuleFromName(const std::string& name) const;
const xsd::Module* getPrimaryModule() const { return mPrimaryModule; }
const std::set<std::string>& getModulesWithDevicesNames() const {
return mModulesWithDevicesNames;
}
- bool haveInputProfilesInModule(const std::string& name) const {
- auto module = getModuleFromName(name);
- if (module && module->getFirstMixPorts()) {
- for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
- if (mixPort.getRole() == xsd::Role::sink) return true;
- }
- }
- return false;
+ std::string getAttachedSinkDeviceForMixPort(const std::string& moduleName,
+ const std::string& mixPortName) const {
+ return findAttachedDevice(getAttachedDevices(moduleName),
+ getSinkDevicesForMixPort(moduleName, mixPortName));
}
+ std::string getAttachedSourceDeviceForMixPort(const std::string& moduleName,
+ const std::string& mixPortName) const {
+ return findAttachedDevice(getAttachedDevices(moduleName),
+ getSourceDevicesForMixPort(moduleName, mixPortName));
+ }
+ std::optional<DeviceAddress> getSinkDeviceForMixPort(const std::string& moduleName,
+ const std::string& mixPortName) const;
+ std::optional<DeviceAddress> getSourceDeviceForMixPort(const std::string& moduleName,
+ const std::string& mixPortName) const;
+ bool haveInputProfilesInModule(const std::string& name) const;
private:
- static std::string findExistingConfigurationFile(const std::string& fileName) {
- for (const auto& location : android::audio_get_configuration_paths()) {
- std::string path = location + '/' + fileName;
- if (access(path.c_str(), F_OK) == 0) {
- return path;
- }
- }
- return std::string{};
- }
- void init() {
- if (mConfig) {
- mStatus = OK;
- mPrimaryModule = getModuleFromName(DeviceManager::kPrimaryDevice);
- if (mConfig->getFirstModules()) {
- for (const auto& module : mConfig->getFirstModules()->get_module()) {
- if (module.getFirstAttachedDevices()) {
- auto attachedDevices = module.getFirstAttachedDevices()->getItem();
- if (!attachedDevices.empty()) {
- mModulesWithDevicesNames.insert(module.getName());
- }
- }
- }
- }
- }
- }
+ static std::string findExistingConfigurationFile(const std::string& fileName);
+ std::string findAttachedDevice(const std::vector<std::string>& attachedDevices,
+ const std::set<std::string>& possibleDevices) const;
+ const std::vector<std::string>& getAttachedDevices(const std::string& moduleName) const;
+ std::optional<DeviceAddress> getDeviceAddressOfDevicePort(
+ const std::string& moduleName, const std::string& devicePortName) const;
+ std::string getDevicePortTagNameFromType(const std::string& moduleName,
+ const AudioDevice& deviceType) const;
+ std::set<std::string> getSinkDevicesForMixPort(const std::string& moduleName,
+ const std::string& mixPortName) const;
+ std::set<std::string> getSourceDevicesForMixPort(const std::string& moduleName,
+ const std::string& mixPortName) const;
+ void init();
const std::string mConfigFileName;
const std::string mFilePath;
std::optional<xsd::AudioPolicyConfiguration> mConfig;
- status_t mStatus = NO_INIT;
+ android::status_t mStatus = android::NO_INIT;
const xsd::Module* mPrimaryModule;
std::set<std::string> mModulesWithDevicesNames;
};
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index 926a791..e446a7f 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -156,6 +156,7 @@
srcs: [
"7.0/AudioPrimaryHidlHalTest.cpp",
"7.0/Generators.cpp",
+ "7.0/PolicyConfig.cpp",
],
generated_headers: ["audio_policy_configuration_V7_0_parser"],
generated_sources: ["audio_policy_configuration_V7_0_parser"],
@@ -163,6 +164,7 @@
"android.hardware.audio@7.0",
"android.hardware.audio.common@7.0",
"android.hardware.audio.common@7.0-enums",
+ "android.hardware.audio.common@7.0-util",
],
cflags: [
"-DMAJOR_VERSION=7",
@@ -178,7 +180,15 @@
}
// Note: the following aren't VTS tests, but rather unit tests
-// to verify correctness of test parameter generator utilities.
+// to verify correctness of test utilities.
+cc_test {
+ name: "HalAudioStreamWorkerTest",
+ host_supported: true,
+ srcs: [
+ "tests/streamworker_tests.cpp",
+ ],
+}
+
cc_test {
name: "HalAudioV6_0GeneratorTest",
defaults: ["VtsHalAudioTargetTest_defaults"],
@@ -210,6 +220,7 @@
defaults: ["VtsHalAudioTargetTest_defaults"],
srcs: [
"7.0/Generators.cpp",
+ "7.0/PolicyConfig.cpp",
"tests/generators_tests.cpp",
],
generated_headers: ["audio_policy_configuration_V7_0_parser"],
@@ -218,6 +229,7 @@
"android.hardware.audio@7.0",
"android.hardware.audio.common@7.0",
"android.hardware.audio.common@7.0-enums",
+ "android.hardware.audio.common@7.0-util",
],
cflags: [
"-DMAJOR_VERSION=7",
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index 56939fe..aa7fd8e 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -89,6 +89,10 @@
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::common::test::utility;
using namespace ::android::hardware::audio::CPP_VERSION;
+using ReadParameters = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadParameters;
+using ReadStatus = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadStatus;
+using WriteCommand = ::android::hardware::audio::CPP_VERSION::IStreamOut::WriteCommand;
+using WriteStatus = ::android::hardware::audio::CPP_VERSION::IStreamOut::WriteStatus;
#if MAJOR_VERSION >= 7
// Make an alias for enumerations generated from the APM config XSD.
namespace xsd {
@@ -100,6 +104,7 @@
static auto okOrNotSupported = {Result::OK, Result::NOT_SUPPORTED};
static auto okOrNotSupportedOrInvalidArgs = {Result::OK, Result::NOT_SUPPORTED,
Result::INVALID_ARGUMENTS};
+static auto okOrInvalidState = {Result::OK, Result::INVALID_STATE};
static auto okOrInvalidStateOrNotSupported = {Result::OK, Result::INVALID_STATE,
Result::NOT_SUPPORTED};
static auto invalidArgsOrNotSupported = {Result::INVALID_ARGUMENTS, Result::NOT_SUPPORTED};
@@ -115,6 +120,7 @@
#include "7.0/Generators.h"
#include "7.0/PolicyConfig.h"
#endif
+#include "StreamWorker.h"
class HidlTest : public ::testing::Test {
public:
@@ -778,6 +784,11 @@
////////////////////////// open{Output,Input}Stream //////////////////////////
//////////////////////////////////////////////////////////////////////////////
+static inline AudioIoHandle getNextIoHandle() {
+ static AudioIoHandle lastHandle{};
+ return ++lastHandle;
+}
+
// This class is also used by some device tests.
template <class Stream>
class StreamHelper {
@@ -787,16 +798,13 @@
template <class Open>
void open(Open openStream, const AudioConfig& config, Result* res,
AudioConfig* suggestedConfigPtr) {
- // FIXME: Open a stream without an IOHandle
- // This is not required to be accepted by hal implementations
- AudioIoHandle ioHandle{};
AudioConfig suggestedConfig{};
bool retryWithSuggestedConfig = true;
if (suggestedConfigPtr == nullptr) {
suggestedConfigPtr = &suggestedConfig;
retryWithSuggestedConfig = false;
}
- ASSERT_OK(openStream(ioHandle, config, returnIn(*res, mStream, *suggestedConfigPtr)));
+ ASSERT_OK(openStream(mIoHandle, config, returnIn(*res, mStream, *suggestedConfigPtr)));
switch (*res) {
case Result::OK:
ASSERT_TRUE(mStream != nullptr);
@@ -806,7 +814,7 @@
ASSERT_TRUE(mStream == nullptr);
if (retryWithSuggestedConfig) {
AudioConfig suggestedConfigRetry;
- ASSERT_OK(openStream(ioHandle, *suggestedConfigPtr,
+ ASSERT_OK(openStream(mIoHandle, *suggestedConfigPtr,
returnIn(*res, mStream, suggestedConfigRetry)));
ASSERT_OK(*res);
ASSERT_TRUE(mStream != nullptr);
@@ -834,8 +842,10 @@
#endif
}
}
+ AudioIoHandle getIoHandle() const { return mIoHandle; }
private:
+ const AudioIoHandle mIoHandle = getNextIoHandle();
sp<Stream>& mStream;
};
@@ -861,7 +871,6 @@
return res;
}
- private:
void TearDown() override {
if (open) {
ASSERT_OK(closeStream());
@@ -879,6 +888,116 @@
////////////////////////////// openOutputStream //////////////////////////////
+class StreamWriter : public StreamWorker<StreamWriter> {
+ public:
+ StreamWriter(IStreamOut* stream, size_t bufferSize)
+ : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {}
+ ~StreamWriter() {
+ stop();
+ if (mEfGroup) {
+ EventFlag::deleteEventFlag(&mEfGroup);
+ }
+ }
+
+ typedef MessageQueue<WriteCommand, ::android::hardware::kSynchronizedReadWrite> CommandMQ;
+ typedef MessageQueue<uint8_t, ::android::hardware::kSynchronizedReadWrite> DataMQ;
+ typedef MessageQueue<WriteStatus, ::android::hardware::kSynchronizedReadWrite> StatusMQ;
+
+ bool workerInit() {
+ std::unique_ptr<CommandMQ> tempCommandMQ;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ std::unique_ptr<StatusMQ> tempStatusMQ;
+ Result retval;
+ Return<void> ret = mStream->prepareForWriting(
+ 1, mBufferSize,
+ [&](Result r, const CommandMQ::Descriptor& commandMQ,
+ const DataMQ::Descriptor& dataMQ, const StatusMQ::Descriptor& statusMQ,
+ const auto& /*halThreadInfo*/) {
+ retval = r;
+ if (retval == Result::OK) {
+ tempCommandMQ.reset(new CommandMQ(commandMQ));
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ tempStatusMQ.reset(new StatusMQ(statusMQ));
+ if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {
+ EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
+ }
+ }
+ });
+ if (!ret.isOk()) {
+ ALOGE("Transport error while calling prepareForWriting: %s", ret.description().c_str());
+ return false;
+ }
+ if (retval != Result::OK) {
+ ALOGE("Error from prepareForWriting: %d", retval);
+ return false;
+ }
+ if (!tempCommandMQ || !tempCommandMQ->isValid() || !tempDataMQ || !tempDataMQ->isValid() ||
+ !tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) {
+ ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing");
+ ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(),
+ "Command message queue for writing is invalid");
+ ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(),
+ "Data message queue for writing is invalid");
+ ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing");
+ ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
+ "Status message queue for writing is invalid");
+ ALOGE_IF(!mEfGroup, "Event flag creation for writing failed");
+ return false;
+ }
+ mCommandMQ = std::move(tempCommandMQ);
+ mDataMQ = std::move(tempDataMQ);
+ mStatusMQ = std::move(tempStatusMQ);
+ return true;
+ }
+
+ bool workerCycle() {
+ WriteCommand cmd = WriteCommand::WRITE;
+ if (!mCommandMQ->write(&cmd)) {
+ ALOGE("command message queue write failed");
+ return false;
+ }
+ const size_t dataSize = std::min(mData.size(), mDataMQ->availableToWrite());
+ bool success = mDataMQ->write(mData.data(), dataSize);
+ ALOGE_IF(!success, "data message queue write failed");
+ mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
+
+ uint32_t efState = 0;
+ retry:
+ status_t ret =
+ mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);
+ if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)) {
+ WriteStatus writeStatus;
+ writeStatus.retval = Result::NOT_INITIALIZED;
+ if (!mStatusMQ->read(&writeStatus)) {
+ ALOGE("status message read failed");
+ success = false;
+ }
+ if (writeStatus.retval != Result::OK) {
+ ALOGE("bad write status: %d", writeStatus.retval);
+ success = false;
+ }
+ }
+ if (ret == -EAGAIN || ret == -EINTR) {
+ // Spurious wakeup. This normally retries no more than once.
+ goto retry;
+ } else if (ret) {
+ ALOGE("bad wait status: %d", ret);
+ success = false;
+ }
+ return success;
+ }
+
+ private:
+ IStreamOut* const mStream;
+ const size_t mBufferSize;
+ std::vector<uint8_t> mData;
+ std::unique_ptr<CommandMQ> mCommandMQ;
+ std::unique_ptr<DataMQ> mDataMQ;
+ std::unique_ptr<StatusMQ> mStatusMQ;
+ EventFlag* mEfGroup = nullptr;
+};
+
class OutputStreamTest : public OpenStreamTest<IStreamOut> {
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
@@ -954,13 +1073,138 @@
////////////////////////////// openInputStream //////////////////////////////
+class StreamReader : public StreamWorker<StreamReader> {
+ public:
+ StreamReader(IStreamIn* stream, size_t bufferSize)
+ : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {}
+ ~StreamReader() {
+ stop();
+ if (mEfGroup) {
+ EventFlag::deleteEventFlag(&mEfGroup);
+ }
+ }
+
+ typedef MessageQueue<ReadParameters, ::android::hardware::kSynchronizedReadWrite> CommandMQ;
+ typedef MessageQueue<uint8_t, ::android::hardware::kSynchronizedReadWrite> DataMQ;
+ typedef MessageQueue<ReadStatus, ::android::hardware::kSynchronizedReadWrite> StatusMQ;
+
+ bool workerInit() {
+ std::unique_ptr<CommandMQ> tempCommandMQ;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ std::unique_ptr<StatusMQ> tempStatusMQ;
+ Result retval;
+ Return<void> ret = mStream->prepareForReading(
+ 1, mBufferSize,
+ [&](Result r, const CommandMQ::Descriptor& commandMQ,
+ const DataMQ::Descriptor& dataMQ, const StatusMQ::Descriptor& statusMQ,
+ const auto& /*halThreadInfo*/) {
+ retval = r;
+ if (retval == Result::OK) {
+ tempCommandMQ.reset(new CommandMQ(commandMQ));
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ tempStatusMQ.reset(new StatusMQ(statusMQ));
+ if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {
+ EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
+ }
+ }
+ });
+ if (!ret.isOk()) {
+ ALOGE("Transport error while calling prepareForReading: %s", ret.description().c_str());
+ return false;
+ }
+ if (retval != Result::OK) {
+ ALOGE("Error from prepareForReading: %d", retval);
+ return false;
+ }
+ if (!tempCommandMQ || !tempCommandMQ->isValid() || !tempDataMQ || !tempDataMQ->isValid() ||
+ !tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) {
+ ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for reading");
+ ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(),
+ "Command message queue for reading is invalid");
+ ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for reading");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(),
+ "Data message queue for reading is invalid");
+ ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for reading");
+ ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
+ "Status message queue for reading is invalid");
+ ALOGE_IF(!mEfGroup, "Event flag creation for reading failed");
+ return false;
+ }
+ mCommandMQ = std::move(tempCommandMQ);
+ mDataMQ = std::move(tempDataMQ);
+ mStatusMQ = std::move(tempStatusMQ);
+ return true;
+ }
+
+ bool workerCycle() {
+ ReadParameters params;
+ params.command = IStreamIn::ReadCommand::READ;
+ params.params.read = mBufferSize;
+ if (!mCommandMQ->write(¶ms)) {
+ ALOGE("command message queue write failed");
+ return false;
+ }
+ mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
+
+ uint32_t efState = 0;
+ bool success = true;
+ retry:
+ status_t ret =
+ mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState);
+ if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)) {
+ ReadStatus readStatus;
+ readStatus.retval = Result::NOT_INITIALIZED;
+ if (!mStatusMQ->read(&readStatus)) {
+ ALOGE("status message read failed");
+ success = false;
+ }
+ if (readStatus.retval != Result::OK) {
+ ALOGE("bad read status: %d", readStatus.retval);
+ success = false;
+ }
+ const size_t dataSize = std::min(mData.size(), mDataMQ->availableToRead());
+ if (!mDataMQ->read(mData.data(), dataSize)) {
+ ALOGE("data message queue read failed");
+ success = false;
+ }
+ }
+ if (ret == -EAGAIN || ret == -EINTR) {
+ // Spurious wakeup. This normally retries no more than once.
+ goto retry;
+ } else if (ret) {
+ ALOGE("bad wait status: %d", ret);
+ success = false;
+ }
+ return success;
+ }
+
+ private:
+ IStreamIn* const mStream;
+ const size_t mBufferSize;
+ std::vector<uint8_t> mData;
+ std::unique_ptr<CommandMQ> mCommandMQ;
+ std::unique_ptr<DataMQ> mDataMQ;
+ std::unique_ptr<StatusMQ> mStatusMQ;
+ EventFlag* mEfGroup = nullptr;
+};
+
class InputStreamTest : public OpenStreamTest<IStreamIn> {
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
#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();
@@ -978,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 = {},
@@ -1377,6 +1622,12 @@
uint64_t frames;
uint64_t time;
ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, time)));
+ // Although 'getCapturePosition' is mandatory in V7, legacy implementations
+ // may return -ENOSYS (which is translated to NOT_SUPPORTED) in cases when
+ // the capture position can't be retrieved, e.g. when the stream isn't
+ // running. Because of this, we don't fail when getting NOT_SUPPORTED
+ // in this test. Behavior of 'getCapturePosition' for running streams is
+ // tested in 'PcmOnlyConfigInputStreamTest' for V7.
ASSERT_RESULT(okOrInvalidStateOrNotSupported, res);
if (res == Result::OK) {
ASSERT_EQ(0U, frames);
@@ -1560,15 +1811,19 @@
"If supported, a stream should always succeed to retrieve the "
"presentation position");
uint64_t frames;
- TimeSpec mesureTS;
- ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, mesureTS)));
+ TimeSpec measureTS;
+ ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, measureTS)));
+#if MAJOR_VERSION <= 6
if (res == Result::NOT_SUPPORTED) {
- doc::partialTest("getpresentationPosition is not supported");
+ doc::partialTest("getPresentationPosition is not supported");
return;
}
+#else
+ ASSERT_NE(Result::NOT_SUPPORTED, res) << "getPresentationPosition is mandatory in V7";
+#endif
ASSERT_EQ(0U, frames);
- if (mesureTS.tvNSec == 0 && mesureTS.tvSec == 0) {
+ if (measureTS.tvNSec == 0 && measureTS.tvSec == 0) {
// As the stream has never written a frame yet,
// the timestamp does not really have a meaning, allow to return 0
return;
@@ -1580,8 +1835,8 @@
auto toMicroSec = [](uint64_t sec, auto nsec) { return sec * 1e+6 + nsec / 1e+3; };
auto currentTime = toMicroSec(currentTS.tv_sec, currentTS.tv_nsec);
- auto mesureTime = toMicroSec(mesureTS.tvSec, mesureTS.tvNSec);
- ASSERT_PRED2([](auto c, auto m) { return c - m < 1e+6; }, currentTime, mesureTime);
+ auto measureTime = toMicroSec(measureTS.tvSec, measureTS.tvNSec);
+ ASSERT_PRED2([](auto c, auto m) { return c - m < 1e+6; }, currentTime, measureTime);
}
//////////////////////////////////////////////////////////////////////////////
diff --git a/audio/core/all-versions/vts/functional/AudioTestDefinitions.h b/audio/core/all-versions/vts/functional/AudioTestDefinitions.h
index 5b14a21..aa67630 100644
--- a/audio/core/all-versions/vts/functional/AudioTestDefinitions.h
+++ b/audio/core/all-versions/vts/functional/AudioTestDefinitions.h
@@ -31,15 +31,17 @@
// Nesting a tuple in another tuple allows to use GTest Combine function to generate
// all combinations of devices and configs.
-enum { PARAM_DEVICE, PARAM_CONFIG, PARAM_FLAGS };
#if MAJOR_VERSION <= 6
+enum { PARAM_DEVICE, PARAM_CONFIG, PARAM_FLAGS };
enum { INDEX_INPUT, INDEX_OUTPUT };
using DeviceConfigParameter =
std::tuple<DeviceParameter, android::hardware::audio::common::CPP_VERSION::AudioConfig,
std::variant<android::hardware::audio::common::CPP_VERSION::AudioInputFlag,
android::hardware::audio::common::CPP_VERSION::AudioOutputFlag>>;
#elif MAJOR_VERSION >= 7
+enum { PARAM_DEVICE, PARAM_PORT_NAME, PARAM_CONFIG, PARAM_FLAGS };
using DeviceConfigParameter =
- std::tuple<DeviceParameter, android::hardware::audio::common::CPP_VERSION::AudioConfig,
+ std::tuple<DeviceParameter, std::string,
+ android::hardware::audio::common::CPP_VERSION::AudioConfig,
std::vector<android::hardware::audio::CPP_VERSION::AudioInOutFlag>>;
#endif
diff --git a/audio/core/all-versions/vts/functional/StreamWorker.h b/audio/core/all-versions/vts/functional/StreamWorker.h
new file mode 100644
index 0000000..68a8024
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/StreamWorker.h
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sched.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+template <typename Impl>
+class StreamWorker {
+ enum class WorkerState { STOPPED, RUNNING, PAUSE_REQUESTED, PAUSED, RESUME_REQUESTED, ERROR };
+
+ public:
+ StreamWorker() = default;
+ ~StreamWorker() { stop(); }
+ bool start() {
+ mWorker = std::thread(&StreamWorker::workerThread, this);
+ std::unique_lock<std::mutex> lock(mWorkerLock);
+ mWorkerCv.wait(lock, [&] { return mWorkerState != WorkerState::STOPPED; });
+ return mWorkerState == WorkerState::RUNNING;
+ }
+ void pause() { switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED); }
+ void resume() { switchWorkerStateSync(WorkerState::PAUSED, WorkerState::RESUME_REQUESTED); }
+ bool hasError() {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ return mWorkerState == WorkerState::ERROR;
+ }
+ void stop() {
+ {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ if (mWorkerState == WorkerState::STOPPED) return;
+ mWorkerState = WorkerState::STOPPED;
+ }
+ if (mWorker.joinable()) {
+ mWorker.join();
+ }
+ }
+ bool waitForAtLeastOneCycle() {
+ WorkerState newState;
+ switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED, &newState);
+ if (newState != WorkerState::PAUSED) return false;
+ switchWorkerStateSync(newState, WorkerState::RESUME_REQUESTED, &newState);
+ return newState == WorkerState::RUNNING;
+ }
+
+ // Methods that need to be provided by subclasses:
+ //
+ // Called once at the beginning of the thread loop. Must return
+ // 'true' to enter the thread loop, otherwise the thread loop
+ // exits and the worker switches into the 'error' state.
+ // bool workerInit();
+ //
+ // Called for each thread loop unless the thread is in 'paused' state.
+ // Must return 'true' to continue running, otherwise the thread loop
+ // exits and the worker switches into the 'error' state.
+ // bool workerCycle();
+
+ private:
+ void switchWorkerStateSync(WorkerState oldState, WorkerState newState,
+ WorkerState* finalState = nullptr) {
+ std::unique_lock<std::mutex> lock(mWorkerLock);
+ if (mWorkerState != oldState) {
+ if (finalState) *finalState = mWorkerState;
+ return;
+ }
+ mWorkerState = newState;
+ mWorkerCv.wait(lock, [&] { return mWorkerState != newState; });
+ if (finalState) *finalState = mWorkerState;
+ }
+ void workerThread() {
+ bool success = static_cast<Impl*>(this)->workerInit();
+ {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ mWorkerState = success ? WorkerState::RUNNING : WorkerState::ERROR;
+ }
+ mWorkerCv.notify_one();
+ if (!success) return;
+
+ for (WorkerState state = WorkerState::RUNNING; state != WorkerState::STOPPED;) {
+ bool needToNotify = false;
+ if (state != WorkerState::PAUSED ? static_cast<Impl*>(this)->workerCycle()
+ : (sched_yield(), true)) {
+ //
+ // Pause and resume are synchronous. One worker cycle must complete
+ // before the worker indicates a state change. This is how 'mWorkerState' and
+ // 'state' interact:
+ //
+ // mWorkerState == RUNNING
+ // client sets mWorkerState := PAUSE_REQUESTED
+ // last workerCycle gets executed, state := mWorkerState := PAUSED by us
+ // (or the workers enters the 'error' state if workerCycle fails)
+ // client gets notified about state change in any case
+ // thread is doing a busy wait while 'state == PAUSED'
+ // client sets mWorkerState := RESUME_REQUESTED
+ // state := mWorkerState (RESUME_REQUESTED)
+ // mWorkerState := RUNNING, but we don't notify the client yet
+ // first workerCycle gets executed, the code below triggers a client notification
+ // (or if workerCycle fails, worker enters 'error' state and also notifies)
+ // state := mWorkerState (RUNNING)
+ if (state == WorkerState::RESUME_REQUESTED) {
+ needToNotify = true;
+ }
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ state = mWorkerState;
+ if (mWorkerState == WorkerState::PAUSE_REQUESTED) {
+ state = mWorkerState = WorkerState::PAUSED;
+ needToNotify = true;
+ } else if (mWorkerState == WorkerState::RESUME_REQUESTED) {
+ mWorkerState = WorkerState::RUNNING;
+ }
+ } else {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ if (state == WorkerState::RESUME_REQUESTED ||
+ mWorkerState == WorkerState::PAUSE_REQUESTED) {
+ needToNotify = true;
+ }
+ mWorkerState = WorkerState::ERROR;
+ state = WorkerState::STOPPED;
+ }
+ if (needToNotify) {
+ mWorkerCv.notify_one();
+ }
+ }
+ }
+
+ std::thread mWorker;
+ std::mutex mWorkerLock;
+ std::condition_variable mWorkerCv;
+ WorkerState mWorkerState = WorkerState::STOPPED; // GUARDED_BY(mWorkerLock);
+};
diff --git a/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp b/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
new file mode 100644
index 0000000..925fd33
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 "StreamWorker.h"
+
+#include <sched.h>
+#include <unistd.h>
+#include <atomic>
+
+#include <gtest/gtest.h>
+#define LOG_TAG "StreamWorker_Test"
+#include <log/log.h>
+
+struct TestStream {
+ std::atomic<bool> error = false;
+};
+
+class TestWorker : public StreamWorker<TestWorker> {
+ public:
+ // Use nullptr to test error reporting from the worker thread.
+ explicit TestWorker(TestStream* stream) : mStream(stream) {}
+
+ size_t getWorkerCycles() const { return mWorkerCycles; }
+ bool hasWorkerCycleCalled() const { return mWorkerCycles != 0; }
+ bool hasNoWorkerCycleCalled(useconds_t usec) {
+ const size_t cyclesBefore = mWorkerCycles;
+ usleep(usec);
+ return mWorkerCycles == cyclesBefore;
+ }
+
+ bool workerInit() { return mStream; }
+ bool workerCycle() {
+ do {
+ mWorkerCycles++;
+ } while (mWorkerCycles == 0);
+ return !mStream->error;
+ }
+
+ private:
+ TestStream* const mStream;
+ std::atomic<size_t> mWorkerCycles = 0;
+};
+
+// The parameter specifies whether an extra call to 'stop' is made at the end.
+class StreamWorkerInvalidTest : public testing::TestWithParam<bool> {
+ public:
+ StreamWorkerInvalidTest() : StreamWorkerInvalidTest(nullptr) {}
+ void TearDown() override {
+ if (GetParam()) {
+ worker.stop();
+ }
+ }
+
+ protected:
+ StreamWorkerInvalidTest(TestStream* stream) : testing::TestWithParam<bool>(), worker(stream) {}
+ TestWorker worker;
+};
+
+TEST_P(StreamWorkerInvalidTest, Uninitialized) {
+ EXPECT_FALSE(worker.hasWorkerCycleCalled());
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, UninitializedPauseIgnored) {
+ EXPECT_FALSE(worker.hasError());
+ worker.pause();
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, UninitializedResumeIgnored) {
+ EXPECT_FALSE(worker.hasError());
+ worker.resume();
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, Start) {
+ EXPECT_FALSE(worker.start());
+ EXPECT_FALSE(worker.hasWorkerCycleCalled());
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, PauseIgnored) {
+ EXPECT_FALSE(worker.start());
+ EXPECT_TRUE(worker.hasError());
+ worker.pause();
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, ResumeIgnored) {
+ EXPECT_FALSE(worker.start());
+ EXPECT_TRUE(worker.hasError());
+ worker.resume();
+ EXPECT_TRUE(worker.hasError());
+}
+
+INSTANTIATE_TEST_SUITE_P(StreamWorkerInvalid, StreamWorkerInvalidTest, testing::Bool());
+
+class StreamWorkerTest : public StreamWorkerInvalidTest {
+ public:
+ StreamWorkerTest() : StreamWorkerInvalidTest(&stream) {}
+
+ protected:
+ TestStream stream;
+};
+
+static constexpr unsigned kWorkerIdleCheckTime = 50 * 1000;
+
+TEST_P(StreamWorkerTest, Uninitialized) {
+ EXPECT_FALSE(worker.hasWorkerCycleCalled());
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, Start) {
+ ASSERT_TRUE(worker.start());
+ worker.waitForAtLeastOneCycle();
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, WorkerError) {
+ ASSERT_TRUE(worker.start());
+ stream.error = true;
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, PauseResume) {
+ ASSERT_TRUE(worker.start());
+ worker.waitForAtLeastOneCycle();
+ EXPECT_FALSE(worker.hasError());
+ worker.pause();
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+ EXPECT_FALSE(worker.hasError());
+ const size_t workerCyclesBefore = worker.getWorkerCycles();
+ worker.resume();
+ // 'resume' is synchronous and returns after the worker has looped at least once.
+ EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, StopPaused) {
+ ASSERT_TRUE(worker.start());
+ worker.waitForAtLeastOneCycle();
+ EXPECT_FALSE(worker.hasError());
+ worker.pause();
+ worker.stop();
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, PauseAfterErrorIgnored) {
+ ASSERT_TRUE(worker.start());
+ stream.error = true;
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ worker.pause();
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, ResumeAfterErrorIgnored) {
+ ASSERT_TRUE(worker.start());
+ stream.error = true;
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ worker.resume();
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, WorkerErrorOnResume) {
+ ASSERT_TRUE(worker.start());
+ worker.waitForAtLeastOneCycle();
+ EXPECT_FALSE(worker.hasError());
+ worker.pause();
+ EXPECT_FALSE(worker.hasError());
+ stream.error = true;
+ EXPECT_FALSE(worker.hasError());
+ worker.resume();
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, WaitForAtLeastOneCycle) {
+ ASSERT_TRUE(worker.start());
+ const size_t workerCyclesBefore = worker.getWorkerCycles();
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
+}
+
+TEST_P(StreamWorkerTest, WaitForAtLeastOneCycleError) {
+ ASSERT_TRUE(worker.start());
+ stream.error = true;
+ EXPECT_FALSE(worker.waitForAtLeastOneCycle());
+}
+
+INSTANTIATE_TEST_SUITE_P(StreamWorker, StreamWorkerTest, testing::Bool());
diff --git a/audio/effect/7.0/types.hal b/audio/effect/7.0/types.hal
index bb2d7b3..8f4f885 100644
--- a/audio/effect/7.0/types.hal
+++ b/audio/effect/7.0/types.hal
@@ -220,9 +220,9 @@
*/
uint16_t memoryUsage;
/** Human readable effect name. */
- uint8_t[64] name;
+ string name;
/** Human readable effect implementor name. */
- uint8_t[64] implementor;
+ string implementor;
};
/**
diff --git a/audio/effect/all-versions/default/OWNERS b/audio/effect/all-versions/OWNERS
similarity index 67%
copy from audio/effect/all-versions/default/OWNERS
copy to audio/effect/all-versions/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/effect/all-versions/default/OWNERS
+++ b/audio/effect/all-versions/OWNERS
@@ -1,3 +1,2 @@
elaurent@google.com
-krocard@google.com
mnaganov@google.com
diff --git a/audio/effect/all-versions/default/util/EffectUtils.cpp b/audio/effect/all-versions/default/util/EffectUtils.cpp
index 1c0419a..1156d21 100644
--- a/audio/effect/all-versions/default/util/EffectUtils.cpp
+++ b/audio/effect/all-versions/default/util/EffectUtils.cpp
@@ -16,6 +16,9 @@
#include <memory.h>
+#define LOG_TAG "EffectUtils"
+#include <log/log.h>
+
#include <HidlUtils.h>
#include <UuidUtils.h>
#include <common/all-versions/VersionUtils.h>
@@ -149,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);
@@ -156,23 +182,37 @@
descriptor->flags = EnumBitfield<EffectFlags>(halDescriptor.flags);
descriptor->cpuLoad = halDescriptor.cpuLoad;
descriptor->memoryUsage = halDescriptor.memoryUsage;
+#if MAJOR_VERSION <= 6
memcpy(descriptor->name.data(), halDescriptor.name, descriptor->name.size());
memcpy(descriptor->implementor.data(), halDescriptor.implementor,
descriptor->implementor.size());
+#else
+ descriptor->name = charBufferFromHal(halDescriptor.name);
+ descriptor->implementor = charBufferFromHal(halDescriptor.implementor);
+#endif
return NO_ERROR;
}
status_t EffectUtils::effectDescriptorToHal(const EffectDescriptor& descriptor,
effect_descriptor_t* halDescriptor) {
+ status_t result = NO_ERROR;
UuidUtils::uuidToHal(descriptor.type, &halDescriptor->type);
UuidUtils::uuidToHal(descriptor.uuid, &halDescriptor->uuid);
halDescriptor->flags = static_cast<uint32_t>(descriptor.flags);
halDescriptor->cpuLoad = descriptor.cpuLoad;
halDescriptor->memoryUsage = descriptor.memoryUsage;
+#if MAJOR_VERSION <= 6
memcpy(halDescriptor->name, descriptor.name.data(), descriptor.name.size());
memcpy(halDescriptor->implementor, descriptor.implementor.data(),
descriptor.implementor.size());
- return NO_ERROR;
+#else
+ // 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;
}
} // namespace implementation
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 7eb8cd2..d021fa0 100644
--- a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
+++ b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
@@ -134,11 +134,40 @@
EXPECT_EQ(format, formatBackIn);
}
+TEST(EffectUtils, ConvertInvalidDescriptor) {
+ effect_descriptor_t halDesc;
+ EffectDescriptor longName{};
+ longName.name = std::string(EFFECT_STRING_LEN_MAX, 'x');
+ EXPECT_EQ(BAD_VALUE, EffectUtils::effectDescriptorToHal(longName, &halDesc));
+ EffectDescriptor longImplementor{};
+ longImplementor.implementor = std::string(EFFECT_STRING_LEN_MAX, 'x');
+ EXPECT_EQ(BAD_VALUE, EffectUtils::effectDescriptorToHal(longImplementor, &halDesc));
+}
+
TEST(EffectUtils, ConvertDescriptor) {
EffectDescriptor desc{};
+ desc.name = "test";
+ desc.implementor = "foo";
effect_descriptor_t halDesc;
EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorToHal(desc, &halDesc));
EffectDescriptor descBack;
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/audio/effect/all-versions/vts/OWNERS b/audio/effect/all-versions/vts/OWNERS
deleted file mode 100644
index 0ea4666..0000000
--- a/audio/effect/all-versions/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-elaurent@google.com
-krocard@google.com
-mnaganov@google.com
-yim@google.com
-zhuoyao@google.com
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 5915a53..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
@@ -490,6 +490,14 @@
{.config =
{
+ .prop = toInt(VehicleProperty::PARKING_BRAKE_AUTO_APPLY),
+ .access = VehiclePropertyAccess::READ,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ },
+ .initialValue = {.int32Values = {1}}},
+
+ {.config =
+ {
.prop = toInt(VehicleProperty::FUEL_LEVEL_LOW),
.access = VehiclePropertyAccess::READ,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
@@ -1059,7 +1067,7 @@
{.config =
{
.prop = toInt(VehicleProperty::SUPPORT_CUSTOMIZE_VENDOR_PERMISSION),
- .access = VehiclePropertyAccess::READ_WRITE,
+ .access = VehiclePropertyAccess::READ,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
.configArray =
{kMixedTypePropertyForTest,
@@ -1141,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,
@@ -1198,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/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index c83e2de..1608e52 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -276,17 +276,6 @@
return false;
}
-// determine if it's running inside Android Emulator
-static bool isInEmulator() {
- char propValue[PROP_VALUE_MAX];
- bool isEmulator = (__system_property_get("ro.kernel.qemu", propValue) != 0);
- if (!isEmulator) {
- isEmulator = (__system_property_get("ro.hardware", propValue) != 0) &&
- (!strcmp(propValue, "ranchu") || !strcmp(propValue, "goldfish"));
- }
- return isEmulator;
-}
-
// Parse supported properties list and generate vector of property values to hold current values.
void EmulatedVehicleHal::onCreate() {
static constexpr bool shouldUpdateStatus = true;
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp
index 548285a..9be9ea7 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp
@@ -31,6 +31,14 @@
GeneratorHub::GeneratorHub(const OnHalEvent& onHalEvent)
: mOnHalEvent(onHalEvent), mThread(&GeneratorHub::run, this) {}
+GeneratorHub::~GeneratorHub() {
+ mShuttingDownFlag.store(true);
+ mCond.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
void GeneratorHub::registerGenerator(int32_t cookie, FakeValueGeneratorPtr generator) {
{
std::lock_guard<std::mutex> g(mLock);
@@ -58,15 +66,18 @@
}
void GeneratorHub::run() {
- while (true) {
+ while (!mShuttingDownFlag.load()) {
std::unique_lock<std::mutex> g(mLock);
// Pop events whose generator does not exist (may be already unregistered)
while (!mEventQueue.empty()
&& mGenerators.find(mEventQueue.top().cookie) == mGenerators.end()) {
mEventQueue.pop();
}
- // Wait until event queue is not empty
- mCond.wait(g, [this] { return !mEventQueue.empty(); });
+ // Wait until event queue is not empty or shutting down flag is set
+ mCond.wait(g, [this] { return !mEventQueue.empty() || mShuttingDownFlag.load(); });
+ if (mShuttingDownFlag.load()) {
+ break;
+ }
const VhalEvent& curEvent = mEventQueue.top();
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.h
index dcf6a4f..b25dbf1 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.h
@@ -58,7 +58,7 @@
public:
GeneratorHub(const OnHalEvent& onHalEvent);
- ~GeneratorHub() = default;
+ ~GeneratorHub();
/**
* Register a new generator. The generator will be discarded if it could not produce next event.
@@ -84,6 +84,7 @@
mutable std::mutex mLock;
std::condition_variable mCond;
std::thread mThread;
+ std::atomic<bool> mShuttingDownFlag{false};
};
} // namespace impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
index 263ca62..f7d0854 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
@@ -44,7 +44,7 @@
mSocketComm = std::make_unique<SocketComm>(this);
mSocketComm->start();
- if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+ if (isInEmulator()) {
ALOGI("Starting PipeComm");
mPipeComm = std::make_unique<PipeComm>(this);
mPipeComm->start();
@@ -226,6 +226,10 @@
return proto_msg_converter::toProto(protoVal, *val);
}
+bool isInEmulator() {
+ return android::base::GetBoolProperty("ro.boot.qemu", false);
+}
+
} // impl
} // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
index 82947be..434d79b 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
@@ -95,6 +95,9 @@
std::unique_ptr<PipeComm> mPipeComm;
};
+// determine if it's running inside Android Emulator
+bool isInEmulator();
+
} // impl
} // namespace V2_0
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 94645a4..1fe4bac 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -688,7 +688,7 @@
* Parking brake state.
*
* @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @access VehiclePropertyAccess:READ
*/
PARKING_BRAKE_ON = (
0x0402
@@ -700,7 +700,7 @@
* Auto-apply parking brake.
*
* @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @access VehiclePropertyAccess:READ
*/
PARKING_BRAKE_AUTO_APPLY = (
0x0403
@@ -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
new file mode 100644
index 0000000..e64e942
--- /dev/null
+++ b/automotive/vehicle/2.0/vts/functional/Android.bp
@@ -0,0 +1,30 @@
+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: [
+ "VtsHalTargetTestDefaults",
+ ],
+ srcs: [
+ "VtsHalAutomotiveVehicleV2_0TargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libhidlbase",
+ "liblog",
+ ],
+ static_libs: [
+ "android.hardware.automotive.vehicle@2.0",
+ ],
+ test_suites: [
+ "vts",
+ "general-tests",
+ ],
+}
diff --git a/automotive/vehicle/2.0/vts/functional/VtsHalAutomotiveVehicleV2_0TargetTest.cpp b/automotive/vehicle/2.0/vts/functional/VtsHalAutomotiveVehicleV2_0TargetTest.cpp
new file mode 100644
index 0000000..7f1d4d1
--- /dev/null
+++ b/automotive/vehicle/2.0/vts/functional/VtsHalAutomotiveVehicleV2_0TargetTest.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VtsHalAutomotiveVehicle"
+
+#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+using namespace android::hardware::automotive::vehicle::V2_0;
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+constexpr auto kTimeout = std::chrono::milliseconds(500);
+constexpr auto kInvalidProp = 0x31600207;
+
+class VtsVehicleCallback : public IVehicleCallback {
+ private:
+ using MutexGuard = std::lock_guard<std::mutex>;
+ using HidlVecOfValues = hidl_vec<VehiclePropValue>;
+ std::mutex mLock;
+ std::condition_variable mEventCond;
+ std::vector<HidlVecOfValues> mReceivedEvents;
+
+ public:
+ Return<void> onPropertyEvent(const hidl_vec<VehiclePropValue>& values) override {
+ {
+ MutexGuard guard(mLock);
+ mReceivedEvents.push_back(values);
+ }
+ mEventCond.notify_one();
+ return Return<void>();
+ }
+
+ Return<void> onPropertySet(const VehiclePropValue& /* value */) override {
+ return Return<void>();
+ }
+ Return<void> onPropertySetError(StatusCode /* errorCode */, int32_t /* propId */,
+ int32_t /* areaId */) override {
+ return Return<void>();
+ }
+
+ bool waitForExpectedEvents(size_t expectedEvents) {
+ std::unique_lock<std::mutex> g(mLock);
+
+ if (expectedEvents == 0 && mReceivedEvents.size() == 0) {
+ return mEventCond.wait_for(g, kTimeout) == std::cv_status::timeout;
+ }
+
+ while (expectedEvents != mReceivedEvents.size()) {
+ if (mEventCond.wait_for(g, kTimeout) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void reset() { mReceivedEvents.clear(); }
+};
+
+class VehicleHalHidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ mVehicle = IVehicle::getService(GetParam());
+ ASSERT_NE(mVehicle.get(), nullptr);
+ }
+ virtual void TearDown() override {}
+
+ sp<IVehicle> mVehicle;
+
+ bool isBooleanGlobalProp(int32_t property) {
+ return (property & (int)VehiclePropertyType::MASK) == (int)VehiclePropertyType::BOOLEAN &&
+ (property & (int)VehicleArea::MASK) == (int)VehicleArea::GLOBAL;
+ }
+
+ void invokeGet(int32_t property, int32_t areaId) {
+ VehiclePropValue requestedValue{};
+ requestedValue.prop = property;
+ requestedValue.areaId = areaId;
+
+ invokeGet(requestedValue);
+ }
+
+ void invokeGet(const VehiclePropValue& requestedPropValue) {
+ mActualValue = VehiclePropValue{}; // reset previous values
+
+ StatusCode refStatus;
+ VehiclePropValue refValue;
+ bool isCalled = false;
+ mVehicle->get(requestedPropValue,
+ [&refStatus, &refValue, &isCalled](StatusCode status,
+ const VehiclePropValue& value) {
+ refStatus = status;
+ refValue = value;
+ isCalled = true;
+ });
+ ASSERT_TRUE(isCalled) << "callback wasn't called for property: " << requestedPropValue.prop;
+
+ mActualValue = refValue;
+ mActualStatusCode = refStatus;
+ }
+
+ VehiclePropValue mActualValue;
+ StatusCode mActualStatusCode;
+};
+
+// Test getAllPropConfig() returns at least 4 property configs.
+TEST_P(VehicleHalHidlTest, getAllPropConfigs) {
+ ALOGD("VehicleHalHidlTest::getAllPropConfigs");
+ bool isCalled = false;
+ hidl_vec<VehiclePropConfig> propConfigs;
+ mVehicle->getAllPropConfigs([&isCalled, &propConfigs](const hidl_vec<VehiclePropConfig>& cfgs) {
+ propConfigs = cfgs;
+ isCalled = true;
+ });
+ ASSERT_TRUE(isCalled);
+ ASSERT_GE(propConfigs.size(), 4);
+}
+
+// Test getPropConfig() can query all properties listed in CDD.
+TEST_P(VehicleHalHidlTest, getPropConfigs) {
+ ALOGD("VehicleHalHidlTest::getPropConfigs");
+ // Check the properties listed in CDD
+ hidl_vec<int32_t> properties = {
+ (int)VehicleProperty::GEAR_SELECTION, (int)VehicleProperty::NIGHT_MODE,
+ (int)VehicleProperty::PARKING_BRAKE_ON, (int)VehicleProperty::PERF_VEHICLE_SPEED};
+ bool isCalled = false;
+ mVehicle->getPropConfigs(
+ properties, [&isCalled](StatusCode status, const hidl_vec<VehiclePropConfig>& cfgs) {
+ ASSERT_EQ(StatusCode::OK, status);
+ ASSERT_EQ(4u, cfgs.size());
+ isCalled = true;
+ });
+ ASSERT_TRUE(isCalled);
+}
+
+// Test getPropConfig() with an invalid propertyId returns an error code.
+TEST_P(VehicleHalHidlTest, getPropConfigsWithInvalidProp) {
+ ALOGD("VehicleHalHidlTest::getPropConfigsWithInvalidProp");
+ hidl_vec<int32_t> properties = {kInvalidProp};
+ bool isCalled = false;
+ mVehicle->getPropConfigs(
+ properties, [&isCalled](StatusCode status, const hidl_vec<VehiclePropConfig>& cfgs) {
+ ASSERT_NE(StatusCode::OK, status);
+ ASSERT_EQ(0, cfgs.size());
+ isCalled = true;
+ });
+ ASSERT_TRUE(isCalled);
+}
+
+// Test get() return current value for properties.
+TEST_P(VehicleHalHidlTest, get) {
+ ALOGD("VehicleHalHidlTest::get");
+ invokeGet((int)VehicleProperty::PERF_VEHICLE_SPEED, 0);
+ ASSERT_EQ(StatusCode::OK, mActualStatusCode);
+}
+
+// Test get() with an invalid propertyId return an error codes.
+TEST_P(VehicleHalHidlTest, getInvalidProp) {
+ ALOGD("VehicleHalHidlTest::getInvalidProp");
+
+ invokeGet(kInvalidProp, 0);
+ ASSERT_NE(StatusCode::OK, mActualStatusCode);
+}
+
+// Test set() on read_write properties.
+TEST_P(VehicleHalHidlTest, setProp) {
+ ALOGD("VehicleHalHidlTest::setProp");
+ hidl_vec<VehiclePropConfig> propConfigs;
+ mVehicle->getAllPropConfigs(
+ [&propConfigs](const hidl_vec<VehiclePropConfig>& cfgs) { propConfigs = cfgs; });
+ for (const VehiclePropConfig& cfg : propConfigs) {
+ // test on boolean and writable property
+ if (cfg.access == VehiclePropertyAccess::READ_WRITE && isBooleanGlobalProp(cfg.prop)) {
+ invokeGet(cfg.prop, 0);
+ int setValue = mActualValue.value.int32Values[0] == 1 ? 0 : 1;
+ VehiclePropValue propToSet = mActualValue;
+ propToSet.value.int32Values[0] = setValue;
+ ASSERT_EQ(StatusCode::OK, mVehicle->set(propToSet));
+ // check set success
+ invokeGet(cfg.prop, 0);
+ ASSERT_EQ(StatusCode::OK, mActualStatusCode);
+ ASSERT_EQ(setValue, mActualValue.value.int32Values[0]);
+ }
+ }
+}
+
+// Test set() on an read_only property.
+TEST_P(VehicleHalHidlTest, setNotWritableProp) {
+ ALOGD("VehicleHalHidlTest::setNotWritableProp");
+ invokeGet(static_cast<int>(VehicleProperty::PERF_VEHICLE_SPEED), 0);
+ ASSERT_EQ(StatusCode::OK, mActualStatusCode);
+ VehiclePropValue vehicleSpeed = mActualValue;
+
+ ASSERT_EQ(StatusCode::ACCESS_DENIED, mVehicle->set(vehicleSpeed));
+}
+
+// Test subscribe() and unsubscribe().
+TEST_P(VehicleHalHidlTest, subscribeAndUnsubscribe) {
+ ALOGD("VehicleHalHidlTest::subscribeAndUnsubscribe");
+ const auto prop = static_cast<int>(VehicleProperty::PERF_VEHICLE_SPEED);
+ sp<VtsVehicleCallback> cb = new VtsVehicleCallback();
+
+ hidl_vec<SubscribeOptions> options = {
+ SubscribeOptions{.propId = prop, 100.0, .flags = SubscribeFlags::EVENTS_FROM_CAR}};
+
+ ASSERT_EQ(StatusCode::OK, mVehicle->subscribe(cb, options));
+ ASSERT_TRUE(cb->waitForExpectedEvents(10));
+
+ ASSERT_EQ(StatusCode::OK, mVehicle->unsubscribe(cb, prop));
+ cb->reset();
+ ASSERT_FALSE(cb->waitForExpectedEvents(10));
+}
+
+// Test subscribe() with an invalid property.
+TEST_P(VehicleHalHidlTest, subscribeInvalidProp) {
+ ALOGD("VehicleHalHidlTest::subscribeInvalidProp");
+
+ sp<VtsVehicleCallback> cb = new VtsVehicleCallback();
+
+ hidl_vec<SubscribeOptions> options = {SubscribeOptions{
+ .propId = kInvalidProp, 10.0, .flags = SubscribeFlags::EVENTS_FROM_CAR}};
+
+ ASSERT_NE(StatusCode::OK, mVehicle->subscribe(cb, options));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, VehicleHalHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVehicle::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/CommonProps.aidl b/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/CommonProps.aidl
index 4b4e7df..d4433c5 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/CommonProps.aidl
+++ b/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/CommonProps.aidl
@@ -37,6 +37,5 @@
int sensorId;
android.hardware.biometrics.common.SensorStrength sensorStrength = android.hardware.biometrics.common.SensorStrength.CONVENIENCE;
int maxEnrollmentsPerUser;
- android.hardware.biometrics.common.HardwareInfo[] hardwareInfo;
- String softwareInfo;
+ android.hardware.biometrics.common.ComponentInfo[] componentInfo;
}
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl b/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/ComponentInfo.aidl
similarity index 94%
rename from biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
rename to biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/ComponentInfo.aidl
index c5288fd..ad11dda 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/ComponentInfo.aidl
@@ -33,8 +33,10 @@
package android.hardware.biometrics.common;
@VintfStability
-parcelable HardwareInfo {
- String deviceName;
+parcelable ComponentInfo {
+ String componentId;
String hardwareVersion;
+ String firmwareVersion;
String serialNumber;
+ String softwareVersion;
}
diff --git a/biometrics/common/aidl/android/hardware/biometrics/common/CommonProps.aidl b/biometrics/common/aidl/android/hardware/biometrics/common/CommonProps.aidl
index 7c3d511..2f5af5d 100644
--- a/biometrics/common/aidl/android/hardware/biometrics/common/CommonProps.aidl
+++ b/biometrics/common/aidl/android/hardware/biometrics/common/CommonProps.aidl
@@ -16,7 +16,7 @@
package android.hardware.biometrics.common;
-import android.hardware.biometrics.common.HardwareInfo;
+import android.hardware.biometrics.common.ComponentInfo;
import android.hardware.biometrics.common.SensorStrength;
@VintfStability
@@ -41,16 +41,7 @@
int maxEnrollmentsPerUser;
/**
- * A list of hardware information for subsystems that pertain to this biometric sensor.
+ * A list of component information for subsystems that pertain to this biometric sensor.
*/
- HardwareInfo[] hardwareInfo;
-
- /**
- * Software information for subsystems that pertain to this biometric sensor.
- * This may include information for the matching algorithm, the PAD (Presentation Attack
- * Detection) algorithm, and any other algorithm(s) used by this biometric sensor.
- * For example, <algorithm_1_info>;<algorithm_2_info>;<algorithm_3_info>.
- * The format of each algorithm's info can be <vendor>/<algorithm>/<version>.
- */
- String softwareInfo;
+ ComponentInfo[] componentInfo;
}
diff --git a/biometrics/common/aidl/android/hardware/biometrics/common/HardwareInfo.aidl b/biometrics/common/aidl/android/hardware/biometrics/common/ComponentInfo.aidl
similarity index 62%
rename from biometrics/common/aidl/android/hardware/biometrics/common/HardwareInfo.aidl
rename to biometrics/common/aidl/android/hardware/biometrics/common/ComponentInfo.aidl
index ce9e354..b268eef 100644
--- a/biometrics/common/aidl/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/biometrics/common/aidl/android/hardware/biometrics/common/ComponentInfo.aidl
@@ -17,19 +17,34 @@
package android.hardware.biometrics.common;
@VintfStability
-parcelable HardwareInfo {
+parcelable ComponentInfo {
/**
* An identifier uniquely identifying a subsystem.
+ * It must not be an empty string.
*/
- String deviceName;
+ String componentId;
/**
* The hardware version. For example, <vendor>/<model>/<revision>.
+ * If there's no hardware version for this component, it must be empty.
*/
String hardwareVersion;
/**
+ * The firmware version.
+ * If there's no firmware version for this component, it must be empty.
+ */
+ String firmwareVersion;
+
+ /**
* The sensor's serial number.
+ * If there's no serial number for this component, it must be empty.
*/
String serialNumber;
+
+ /**
+ * The software version. For example, <vendor>/<version>/<revision>.
+ * If there's no software version for this component, it must be empty.
+ */
+ String softwareVersion;
}
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/IFace.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/IFace.aidl
index 0d1ef45..fc4a4d0 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/IFace.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/IFace.aidl
@@ -36,5 +36,4 @@
interface IFace {
android.hardware.biometrics.face.SensorProps[] getSensorProps();
android.hardware.biometrics.face.ISession createSession(in int sensorId, in int userId, in android.hardware.biometrics.face.ISessionCallback cb);
- void reset();
}
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 d6ebbb6..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);
@@ -53,4 +52,5 @@
void onEnrollmentsRemoved(in int[] enrollmentIds);
void onAuthenticatorIdRetrieved(in long authenticatorId);
void onAuthenticatorIdInvalidated(in long newAuthenticatorId);
+ void onSessionClosed();
}
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
deleted file mode 100644
index 4db47c9..0000000
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/SessionState.aidl
+++ /dev/null
@@ -1,51 +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.
- */
-///////////////////////////////////////////////////////////////////////////////
-// 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.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,
-}
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/IFace.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/IFace.aidl
index afb7c8d..11cdf77 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/IFace.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/IFace.aidl
@@ -50,14 +50,4 @@
* @return A new session.
*/
ISession createSession(in int sensorId, in int userId, in ISessionCallback cb);
-
- /**
- * Resets the HAL into a clean state, forcing it to cancel all of the pending operations, close
- * its current session, and release all of the acquired resources.
- *
- * This should be used as a last resort to recover the HAL if the current session becomes
- * unresponsive. The implementation might choose to restart the HAL process to get back into a
- * good state.
- */
- void reset();
}
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
index 66c7c38..f9c13e6 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,14 @@
* 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.
*
* 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 +228,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 +251,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 +268,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 +307,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 +332,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 +344,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 +375,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 2e3cd95..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.
*
@@ -227,4 +220,10 @@
* current set of enrollments.
*/
void onAuthenticatorIdInvalidated(in long newAuthenticatorId);
+
+ /**
+ * This method notifes the client that this session has closed.
+ * The client must not make any more calls to this session.
+ */
+ void onSessionClosed();
}
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/Face.cpp b/biometrics/face/aidl/default/Face.cpp
index 73e50f3..aca3e13 100644
--- a/biometrics/face/aidl/default/Face.cpp
+++ b/biometrics/face/aidl/default/Face.cpp
@@ -24,23 +24,33 @@
const int kMaxEnrollmentsPerUser = 5;
const FaceSensorType kSensorType = FaceSensorType::RGB;
const bool kHalControlsPreview = true;
-const std::string kHwDeviceName = "faceSensor";
+const std::string kHwComponentId = "faceSensor";
const std::string kHardwareVersion = "vendor/model/revision";
+const std::string kFirmwareVersion = "1.01";
const std::string kSerialNumber = "00000001";
-const std::string kSoftwareVersion = "vendor1/algorithm1/version;vendor2/algorithm2/version";
+const std::string kSwComponentId = "matchingAlgorithm";
+const std::string kSoftwareVersion = "vendor/version/revision";
ndk::ScopedAStatus Face::getSensorProps(std::vector<SensorProps>* return_val) {
- common::HardwareInfo hardware_info;
- hardware_info.deviceName = kHwDeviceName;
- hardware_info.hardwareVersion = kHardwareVersion;
- hardware_info.serialNumber = kSerialNumber;
+ common::ComponentInfo hw_component_info;
+ hw_component_info.componentId = kHwComponentId;
+ hw_component_info.hardwareVersion = kHardwareVersion;
+ hw_component_info.firmwareVersion = kFirmwareVersion;
+ hw_component_info.serialNumber = kSerialNumber;
+ hw_component_info.softwareVersion = "";
+
+ common::ComponentInfo sw_component_info;
+ sw_component_info.componentId = kSwComponentId;
+ sw_component_info.hardwareVersion = "";
+ sw_component_info.firmwareVersion = "";
+ sw_component_info.serialNumber = "";
+ sw_component_info.softwareVersion = kSoftwareVersion;
common::CommonProps commonProps;
commonProps.sensorId = kSensorId;
commonProps.sensorStrength = kSensorStrength;
commonProps.maxEnrollmentsPerUser = kMaxEnrollmentsPerUser;
- commonProps.hardwareInfo = {std::move(hardware_info)};
- commonProps.softwareInfo = kSoftwareVersion;
+ commonProps.componentInfo = {std::move(hw_component_info), std::move(sw_component_info)};
SensorProps props;
props.commonProps = std::move(commonProps);
@@ -63,8 +73,4 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Face::reset() {
- return ndk::ScopedAStatus::ok();
-}
-
} // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/Face.h b/biometrics/face/aidl/default/Face.h
index 809b856..786b4f8 100644
--- a/biometrics/face/aidl/default/Face.h
+++ b/biometrics/face/aidl/default/Face.h
@@ -27,8 +27,6 @@
ndk::ScopedAStatus createSession(int32_t sensorId, int32_t userId,
const std::shared_ptr<ISessionCallback>& cb,
std::shared_ptr<ISession>* _aidl_return) override;
-
- ndk::ScopedAStatus reset() override;
};
} // namespace aidl::android::hardware::biometrics::face
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 4cc8b4a..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,8 +121,15 @@
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> {
@@ -129,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);
@@ -159,6 +173,7 @@
::android::PrintInstanceNameToString);
} // namespace
+} // namespace aidl::android::hardware::biometrics::face
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
@@ -167,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/IFingerprint.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IFingerprint.aidl
index 07777c9..5d3df6f 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IFingerprint.aidl
+++ b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IFingerprint.aidl
@@ -36,5 +36,4 @@
interface IFingerprint {
android.hardware.biometrics.fingerprint.SensorProps[] getSensorProps();
android.hardware.biometrics.fingerprint.ISession createSession(in int sensorId, in int userId, in android.hardware.biometrics.fingerprint.ISessionCallback cb);
- void reset();
}
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 13c2b05..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);
@@ -50,4 +49,5 @@
void onEnrollmentsRemoved(in int[] enrollmentIds);
void onAuthenticatorIdRetrieved(in long authenticatorId);
void onAuthenticatorIdInvalidated(in long newAuthenticatorId);
+ void onSessionClosed();
}
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 37062ba..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
@@ -65,14 +52,4 @@
* @return A new session
*/
ISession createSession(in int sensorId, in int userId, in ISessionCallback cb);
-
- /**
- * Resets the HAL into a clean state, forcing it to cancel all of the pending operations, close
- * its current session, and release all of the acquired resources.
- *
- * This should be used as a last resort to recover the HAL if the current session becomes
- * unresponsive. The implementation might choose to restart the HAL process to get back into a
- * good state.
- */
- void reset();
}
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 fde1df7..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.
*
@@ -200,4 +193,10 @@
* current set of enrollments.
*/
void onAuthenticatorIdInvalidated(in long newAuthenticatorId);
+
+ /**
+ * This method notifes the client that this session has closed.
+ * The client must not make any more calls to this session.
+ */
+ void onSessionClosed();
}
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/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 206f518..fbfa52f 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -26,10 +26,12 @@
constexpr int MAX_ENROLLMENTS_PER_USER = 5;
constexpr FingerprintSensorType SENSOR_TYPE = FingerprintSensorType::REAR;
constexpr bool SUPPORTS_NAVIGATION_GESTURES = true;
-constexpr char HW_DEVICE_NAME[] = "fingerprintSensor";
+constexpr char HW_COMPONENT_ID[] = "fingerprintSensor";
constexpr char HW_VERSION[] = "vendor/model/revision";
+constexpr char FW_VERSION[] = "1.01";
constexpr char SERIAL_NUMBER[] = "00000001";
-constexpr char SW_VERSION[] = "vendor1/algorithm1/version;vendor2/algorithm2/version";
+constexpr char SW_COMPONENT_ID[] = "matchingAlgorithm";
+constexpr char SW_VERSION[] = "vendor/version/revision";
} // namespace
@@ -37,10 +39,13 @@
: mEngine(std::make_unique<FakeFingerprintEngine>()), mWorker(MAX_WORKER_QUEUE_SIZE) {}
ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* out) {
- std::vector<common::HardwareInfo> hardwareInfos = {{HW_DEVICE_NAME, HW_VERSION, SERIAL_NUMBER}};
+ std::vector<common::ComponentInfo> componentInfo = {
+ {HW_COMPONENT_ID, HW_VERSION, FW_VERSION, SERIAL_NUMBER, "" /* softwareVersion */},
+ {SW_COMPONENT_ID, "" /* hardwareVersion */, "" /* firmwareVersion */,
+ "" /* serialNumber */, SW_VERSION}};
common::CommonProps commonProps = {SENSOR_ID, SENSOR_STRENGTH, MAX_ENROLLMENTS_PER_USER,
- hardwareInfos, SW_VERSION};
+ componentInfo};
SensorLocation sensorLocation = {0 /* displayId */, 0 /* sensorLocationX */,
0 /* sensorLocationY */, 0 /* sensorRadius */};
@@ -63,10 +68,4 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Fingerprint::reset() {
- // Crash. The system will start a fresh instance of the HAL.
- CHECK(false) << "Unable to reset. Crashing.";
- return ndk::ScopedAStatus::ok();
-}
-
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index 9e6ac77..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 = mScheduledState;
+ 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,114 +118,113 @@
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->onStateChanged(cookie, mCurrentState);
+ 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/Fingerprint.h b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
index 9b43419..7bd3d6d 100644
--- a/biometrics/fingerprint/aidl/default/include/Fingerprint.h
+++ b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
@@ -34,8 +34,6 @@
const std::shared_ptr<ISessionCallback>& cb,
std::shared_ptr<ISession>* out) override;
- ndk::ScopedAStatus reset() override;
-
private:
std::unique_ptr<FakeFingerprintEngine> mEngine;
WorkerThread mWorker;
diff --git a/biometrics/fingerprint/aidl/default/include/Session.h b/biometrics/fingerprint/aidl/default/include/Session.h
index d2f0c19..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,19 +89,34 @@
// 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;
int32_t mUserId;
+
+ // Callback for talking to the framework. This callback must only be called from non-binder
+ // threads to prevent nested binder calls and consequently a binder thread exhaustion.
+ // Practically, it means that this callback should always be called from the worker thread.
std::shared_ptr<ISessionCallback> mCb;
+
+ // Module that communicates to the actual fingerprint hardware, keystore, TEE, etc. In real
+ // life such modules typically consume a lot of memory and are slow to initialize. This is here
+ // to showcase how such a module can be used within a Session without incurring the high
+ // initialization costs every time a Session is constructed.
FakeFingerprintEngine* mEngine;
+
+ // Worker thread that allows to schedule tasks for asynchronous execution.
WorkerThread* mWorker;
- SessionState mScheduledState;
- SessionState mCurrentState;
+
+ // Simple representation of the session's state machine. These are atomic because they can be
+ // modified from both the main and the worker threads.
+ std::atomic<SessionState> mScheduledState;
+ std::atomic<SessionState> mCurrentState;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp b/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
index 0d5014bb..c548fe5 100644
--- a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
@@ -32,23 +32,41 @@
TEST(WorkerThreadTest, ScheduleReturnsTrueWhenQueueHasSpace) {
WorkerThread worker(1 /*maxQueueSize*/);
for (int i = 0; i < 100; ++i) {
- EXPECT_TRUE(worker.schedule(Callable::from([] {})));
- // Allow enough time for the previous task to be processed.
- std::this_thread::sleep_for(2ms);
+ std::promise<void> promise;
+ auto future = promise.get_future();
+
+ ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise)]() mutable {
+ // Notify that the task has started.
+ promise.set_value();
+ })));
+
+ auto status = future.wait_for(1s);
+ EXPECT_EQ(status, std::future_status::ready);
}
}
TEST(WorkerThreadTest, ScheduleReturnsFalseWhenQueueIsFull) {
WorkerThread worker(2 /*maxQueueSize*/);
- // Add a long-running task.
- worker.schedule(Callable::from([] { std::this_thread::sleep_for(1s); }));
- // Allow enough time for the worker to start working on the previous task.
- std::this_thread::sleep_for(2ms);
+ std::promise<void> promise;
+ auto future = promise.get_future();
+ // Schedule a long-running task.
+ ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise)]() mutable {
+ // Notify that the task has started.
+ promise.set_value();
+ // Block for a "very long" time.
+ std::this_thread::sleep_for(2s);
+ })));
+
+ // Make sure the long-running task began executing.
+ auto status = future.wait_for(1s);
+ ASSERT_EQ(status, std::future_status::ready);
+
+ // The first task is already being worked on, which means the queue must be empty.
// Fill the worker's queue to the maximum.
- worker.schedule(Callable::from([] {}));
- worker.schedule(Callable::from([] {}));
+ ASSERT_TRUE(worker.schedule(Callable::from([] {})));
+ ASSERT_TRUE(worker.schedule(Callable::from([] {})));
EXPECT_FALSE(worker.schedule(Callable::from([] {})));
}
@@ -71,7 +89,8 @@
auto future = promise.get_future();
// Schedule a special task to signal when all of the tasks are finished.
- worker.schedule(Callable::from([&promise] { promise.set_value(); }));
+ worker.schedule(
+ Callable::from([promise = std::move(promise)]() mutable { promise.set_value(); }));
auto status = future.wait_for(1s);
ASSERT_EQ(status, std::future_status::ready);
@@ -84,23 +103,37 @@
std::promise<void> promise2;
auto future1 = promise1.get_future();
auto future2 = promise2.get_future();
+ std::atomic<bool> value;
+ // Local scope for the worker to test its destructor when it goes out of scope.
{
WorkerThread worker(2 /*maxQueueSize*/);
- worker.schedule(Callable::from([&promise1] {
- promise1.set_value();
- std::this_thread::sleep_for(200ms);
- }));
- worker.schedule(Callable::from([&promise2] { promise2.set_value(); }));
- // Make sure the first task is executing.
- auto status1 = future1.wait_for(1s);
- ASSERT_EQ(status1, std::future_status::ready);
+ ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise1)]() mutable {
+ promise.set_value();
+ std::this_thread::sleep_for(200ms);
+ })));
+
+ // The first task should start executing.
+ auto status = future1.wait_for(1s);
+ ASSERT_EQ(status, std::future_status::ready);
+
+ // The second task should schedule successfully.
+ ASSERT_TRUE(
+ worker.schedule(Callable::from([promise = std::move(promise2), &value]() mutable {
+ // The worker should destruct before it gets a chance to execute this.
+ value = true;
+ promise.set_value();
+ })));
}
// The second task should never execute.
- auto status2 = future2.wait_for(1s);
- EXPECT_EQ(status2, std::future_status::timeout);
+ auto status = future2.wait_for(1s);
+ ASSERT_EQ(status, std::future_status::ready);
+ // The future is expected to be ready but contain an exception.
+ // Cannot use ASSERT_THROW because exceptions are disabled in this codebase.
+ // ASSERT_THROW(future2.get(), std::future_error);
+ EXPECT_FALSE(value);
}
} // namespace
diff --git a/biometrics/fingerprint/aidl/vts/VtsHalBiometricsFingerprintTargetTest.cpp b/biometrics/fingerprint/aidl/vts/VtsHalBiometricsFingerprintTargetTest.cpp
index 894fdfe..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,10 +93,13 @@
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> {
@@ -137,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/audio/2.1/types.hal b/bluetooth/audio/2.1/types.hal
index 5604c38..e0dcc02 100644
--- a/bluetooth/audio/2.1/types.hal
+++ b/bluetooth/audio/2.1/types.hal
@@ -16,13 +16,14 @@
package android.hardware.bluetooth.audio@2.1;
-import @2.0::PcmParameters;
-import @2.0::SessionType;
-import @2.0::SampleRate;
-import @2.0::ChannelMode;
import @2.0::BitsPerSample;
-import @2.0::CodecConfiguration;
+import @2.0::ChannelMode;
import @2.0::CodecCapabilities;
+import @2.0::CodecConfiguration;
+import @2.0::CodecType;
+import @2.0::PcmParameters;
+import @2.0::SampleRate;
+import @2.0::SessionType;
enum SessionType : @2.0::SessionType {
/** Used when encoded by Bluetooth Stack and streaming to LE Audio device */
@@ -35,6 +36,10 @@
LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH,
};
+enum CodecType : @2.0::CodecType {
+ LC3 = 0x20,
+};
+
enum SampleRate : @2.0::SampleRate {
RATE_8000 = 0x100,
RATE_32000 = 0x200,
@@ -49,14 +54,57 @@
uint32_t dataIntervalUs;
};
-/** Used to configure either a Hardware or Software Encoding session based on session type */
-safe_union AudioConfiguration {
- PcmParameters pcmConfig;
- CodecConfiguration codecConfig;
+enum Lc3FrameDuration : uint8_t {
+ DURATION_10000US = 0x00,
+ DURATION_7500US = 0x01,
+};
+
+/**
+ * Used for Hardware Encoding/Decoding LC3 codec parameters.
+ */
+struct Lc3Parameters {
+ /* PCM is Input for encoder, Output for decoder */
+ BitsPerSample pcmBitDepth;
+
+ /* codec-specific parameters */
+ SampleRate samplingFrequency;
+ Lc3FrameDuration frameDuration;
+ /* length in octets of a codec frame */
+ uint32_t octetsPerFrame;
+ /* Number of blocks of codec frames per single SDU (Service Data Unit) */
+ uint8_t blocksPerSdu;
+};
+
+/**
+ * Used to specify the capabilities of the LC3 codecs supported by Hardware Encoding.
+ */
+struct Lc3CodecCapabilities {
+ /* This is bitfield, if bit N is set, HW Offloader supports N+1 channels at the same time.
+ * Example: 0x27 = 0b00100111: One, two, three or six channels supported.*/
+ uint8_t supportedChannelCounts;
+ Lc3Parameters lc3Capabilities;
};
/** Used to specify the capabilities of the different session types */
safe_union AudioCapabilities {
PcmParameters pcmCapabilities;
CodecCapabilities codecCapabilities;
+ Lc3CodecCapabilities leAudioCapabilities;
};
+
+/**
+ * Used to configure a LC3 Hardware Encoding session.
+ */
+struct Lc3CodecConfiguration {
+ /* This is also bitfield, specifying how the channels are ordered in the outgoing media packet.
+ * Bit meaning is defined in Bluetooth Assigned Numbers. */
+ uint32_t audioChannelAllocation;
+ Lc3Parameters lc3Config;
+};
+
+/** Used to configure either a Hardware or Software Encoding session based on session type */
+safe_union AudioConfiguration {
+ PcmParameters pcmConfig;
+ CodecConfiguration codecConfig;
+ Lc3CodecConfiguration leAudioCodecConfig;
+};
\ No newline at end of file
diff --git a/broadcastradio/1.0/default/OWNERS b/broadcastradio/1.0/default/OWNERS
index b159083..57e6592 100644
--- a/broadcastradio/1.0/default/OWNERS
+++ b/broadcastradio/1.0/default/OWNERS
@@ -1,4 +1,3 @@
elaurent@google.com
-krocard@google.com
mnaganov@google.com
twasilczyk@google.com
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index 5ba7a76..362ab41 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -495,7 +495,7 @@
* invoked carrying a proper selector;
* - program changes exactly to what was requested.
*/
-TEST_F(BroadcastRadioHalTest, DabTune) {
+TEST_P(BroadcastRadioHalTest, DabTune) {
ASSERT_TRUE(openSession());
ProgramSelector sel = {};
diff --git a/camera/device/3.7/Android.bp b/camera/device/3.7/Android.bp
index 42782f2..15c1b5c 100644
--- a/camera/device/3.7/Android.bp
+++ b/camera/device/3.7/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.camera.device@3.7",
root: "android.hardware",
@@ -15,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/camera/provider/2.7/Android.bp b/camera/provider/2.7/Android.bp
index 094dd9d..ba59b38 100644
--- a/camera/provider/2.7/Android.bp
+++ b/camera/provider/2.7/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.camera.provider@2.7",
root: "android.hardware",
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 9e99c48..c656af2 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -359,7 +359,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.media.c2</name>
- <version>1.0-1</version>
+ <version>1.0-2</version>
<interface>
<name>IComponentStore</name>
<regex-instance>default[0-9]*</regex-instance>
diff --git a/contexthub/1.2/IContexthub.hal b/contexthub/1.2/IContexthub.hal
index 4bb9361..04a8cb2 100644
--- a/contexthub/1.2/IContexthub.hal
+++ b/contexthub/1.2/IContexthub.hal
@@ -51,18 +51,6 @@
registerCallback_1_2(uint32_t hubId, IContexthubCallback cb) generates (Result result);
/**
- * Send a message to a hub
- *
- * @param hubId identifier for hub to send message to
- * @param msg message to be sent
- *
- * @return result OK if successful, error code otherwise
- * BAD_VALUE if parameters are not valid
- * TRANSACTION_FAILED if message send failed
- */
- sendMessageToHub_1_2(uint32_t hubId, ContextHubMsg msg) generates (Result result);
-
- /**
* Notification sent by the framework to indicate that the user
* has changed a setting.
*
diff --git a/contexthub/1.2/default/Contexthub.cpp b/contexthub/1.2/default/Contexthub.cpp
index 601eccd..57145fc 100644
--- a/contexthub/1.2/default/Contexthub.cpp
+++ b/contexthub/1.2/default/Contexthub.cpp
@@ -80,12 +80,6 @@
return Result::BAD_PARAMS;
}
-// We don't expose any nanoapps, therefore all nanoapp-related API calls return with BAD_PARAMS
-Return<Result> Contexthub::sendMessageToHub_1_2(uint32_t /* hubId */,
- const ContextHubMsg& /* msg */) {
- return Result::BAD_PARAMS;
-}
-
Return<void> Contexthub::onSettingChanged(SettingV1_1 /*setting*/, SettingValue /*newValue*/) {
return Void();
}
diff --git a/contexthub/1.2/default/Contexthub.h b/contexthub/1.2/default/Contexthub.h
index 32b862d..305544d 100644
--- a/contexthub/1.2/default/Contexthub.h
+++ b/contexthub/1.2/default/Contexthub.h
@@ -55,8 +55,6 @@
Return<Result> registerCallback_1_2(uint32_t hubId,
const sp<V1_2::IContexthubCallback>& cb) override;
- Return<Result> sendMessageToHub_1_2(uint32_t hubId, const ContextHubMsg& msg) override;
-
private:
sp<IContextHubCallbackWrapperBase> mCallback;
};
diff --git a/contexthub/1.2/types.hal b/contexthub/1.2/types.hal
index 5a11efe..75122bc 100644
--- a/contexthub/1.2/types.hal
+++ b/contexthub/1.2/types.hal
@@ -35,21 +35,20 @@
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 {
@1.0::ContextHubMsg msg_1_0;
/**
- * The list of Android permissions that the sender of this message has at
- * the time the message was sent.
- *
- * The HAL MUST drop messages to nanoapps if this list of permissions is not
- * a superset of those of the receiving nanoapp(s).
+ * The list of Android permissions held by the sending nanoapp at the time
+ * the message was sent.
*
* The framework MUST drop messages to host apps that don't have a superset
* of the permissions that the sending nanoapp is using.
diff --git a/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp b/contexthub/1.2/vts/functional/VtsHalContexthubV1_2TargetTest.cpp
index c50d43c..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));
}
@@ -195,39 +195,8 @@
std::promise<TransactionResult> promise;
};
-// Parameterized fixture that sets the callback to TxnResultCallback
-class ContexthubTxnTest : public ContexthubHidlTest {
- public:
- virtual void SetUp() override {
- ContexthubHidlTest::SetUp();
- ASSERT_OK(registerCallback_1_2(cb));
- }
-
- sp<TxnResultCallback> cb = new TxnResultCallback();
-};
-
-TEST_P(ContexthubTxnTest, TestSendMessageToNonExistentNanoApp) {
- ContextHubMsg msg;
- msg.msg_1_0.appName = kNonExistentAppId;
- msg.msg_1_0.msgType = 1;
- msg.msg_1_0.msg.resize(4);
- std::fill(msg.msg_1_0.msg.begin(), msg.msg_1_0.msg.end(), 0);
-
- ALOGD("Sending message to non-existent nanoapp");
- Result result = hubApi->sendMessageToHub_1_2(getHubId(), msg);
- if (result != Result::OK && result != Result::BAD_PARAMS &&
- result != Result::TRANSACTION_FAILED) {
- FAIL() << "Got result " << asBaseType(result) << ", expected OK, BAD_PARAMS"
- << ", or TRANSACTION_FAILED";
- }
-}
-
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContexthubHidlTest);
INSTANTIATE_TEST_SUITE_P(HubIdSpecificTests, ContexthubHidlTest, testing::ValuesIn(kTestParameters),
android::hardware::PrintInstanceTupleNameToString<>);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContexthubTxnTest);
-INSTANTIATE_TEST_SUITE_P(HubIdSpecificTests, ContexthubTxnTest, testing::ValuesIn(kTestParameters),
- android::hardware::PrintInstanceTupleNameToString<>);
-
} // anonymous namespace
diff --git a/drm/1.4/types.hal b/drm/1.4/types.hal
index a4490a5..8cb27cd 100644
--- a/drm/1.4/types.hal
+++ b/drm/1.4/types.hal
@@ -32,8 +32,16 @@
/**
* Returned by getLogMessages to report error diagnostics to the
* app.
+ *
+ * The |message| field is for informational purposes only, and
+ * NOT meant to be parsed programmatically when handling errors.
+ * For programmatic error handling, please check the return |Status|
+ * of APIs instead.
*/
struct LogMessage {
+ /**
+ * Epoch time in milliseconds.
+ */
int64_t timeMs;
LogPriority priority;
string message;
diff --git a/gnss/2.1/default/Android.bp b/gnss/2.1/default/Android.bp
index 9a44eb5..609f59e 100644
--- a/gnss/2.1/default/Android.bp
+++ b/gnss/2.1/default/Android.bp
@@ -33,6 +33,7 @@
"service.cpp",
],
shared_libs: [
+ "libcutils",
"libhidlbase",
"libutils",
"liblog",
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/BlocklistedSource.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/BlocklistedSource.aidl
index 1a2feb7..a4f0097 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/BlocklistedSource.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/BlocklistedSource.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/CorrelationVector.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/CorrelationVector.aidl
index 9c9a241..b0848bb 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/CorrelationVector.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/CorrelationVector.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl
index fbcc8a3..7d3baa4 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl
index 15fe2e9..c54cc2c 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssConstellationType.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssConstellationType.aidl
index f873b87..c1fcfcc 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssConstellationType.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssConstellationType.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl
index 9e05db7..ebb5d0b 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl
index bc73bb1..948c540 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl
index b9db343..24f45c4 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssPowerStats.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssPowerStats.aidl
index 6676e2e..670244f 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssPowerStats.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssPowerStats.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl
index ba02f72..c2a5b51 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
index 10b05a7..f93b496 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
index 9ad9159..fb0931c 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -37,4 +38,5 @@
const int CAPABILITY_SATELLITE_BLOCKLIST = 512;
const int CAPABILITY_CORRELATION_VECTOR = 4096;
const int CAPABILITY_SATELLITE_PVT = 8192;
+ const int CAPABILITY_MEASUREMENT_CORRECTIONS_FOR_DRIVING = 16384;
}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssConfiguration.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssConfiguration.aidl
index 20dd781..54cd022 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssConfiguration.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssConfiguration.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl
index 93894bf..6e62617 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl
index 07a51ae..24d6f9c 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndication.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndication.aidl
index 1cc7f71..fbf1f6f 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndication.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndication.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndicationCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndicationCallback.aidl
index 01f91dc..bfa787e 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndicationCallback.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndicationCallback.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPsds.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPsds.aidl
index 1758792..526ecc8 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPsds.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPsds.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPsdsCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPsdsCallback.aidl
index a1af105..2205bc4 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPsdsCallback.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPsdsCallback.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/PsdsType.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/PsdsType.aidl
index b387121..727bb69 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/PsdsType.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/PsdsType.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatelliteClockInfo.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatelliteClockInfo.aidl
index 4dd45b2..ed23e63 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatelliteClockInfo.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatelliteClockInfo.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePositionEcef.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePositionEcef.aidl
index c96b2e5..e1a20c3 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePositionEcef.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePositionEcef.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePvt.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePvt.aidl
index 95af152..747ee90 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePvt.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePvt.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatelliteVelocityEcef.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatelliteVelocityEcef.aidl
index 558aec9..a571048 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatelliteVelocityEcef.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatelliteVelocityEcef.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
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/IGnssCallback.aidl b/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
index 8881ea7..aad09ef 100644
--- a/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
@@ -16,8 +16,8 @@
package android.hardware.gnss;
-import android.hardware.gnss.IGnssPsds;
import android.hardware.gnss.IGnssConfiguration;
+import android.hardware.gnss.IGnssPsds;
/**
* This interface is required for the HAL to communicate certain information
@@ -26,15 +26,17 @@
*/
@VintfStability
interface IGnssCallback {
-
/** Capability bit mask indicating that GNSS supports blocklisting satellites */
const int CAPABILITY_SATELLITE_BLOCKLIST = 1 << 9;
/** Capability bit mask indicating that GNSS supports correlation vector */
- const int CAPABILITY_CORRELATION_VECTOR = 1 << 12;
+ const int CAPABILITY_CORRELATION_VECTOR = 1 << 12;
/** Capability bit mask indicating that GNSS supports satellite PVT */
- const int CAPABILITY_SATELLITE_PVT = 1 << 13;
+ const int CAPABILITY_SATELLITE_PVT = 1 << 13;
+
+ /** Capability bit mask indicating that GNSS supports measurement corrections for driving */
+ const int CAPABILITY_MEASUREMENT_CORRECTIONS_FOR_DRIVING = 1 << 14;
/**
* Callback to inform framework of the GNSS HAL implementation's capabilities.
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/aidl/default/Android.bp b/gnss/aidl/default/Android.bp
index d363a9f..4cc2b6e 100644
--- a/gnss/aidl/default/Android.bp
+++ b/gnss/aidl/default/Android.bp
@@ -40,6 +40,7 @@
],
shared_libs: [
"libbase",
+ "libcutils",
"libbinder_ndk",
"libhidlbase",
"libutils",
diff --git a/gnss/common/utils/default/Android.bp b/gnss/common/utils/default/Android.bp
index a330c5a..43db873 100644
--- a/gnss/common/utils/default/Android.bp
+++ b/gnss/common/utils/default/Android.bp
@@ -44,6 +44,7 @@
],
export_include_dirs: ["include"],
shared_libs: [
+ "libcutils",
"libhidlbase",
"libutils",
"android.hardware.gnss@1.0",
diff --git a/gnss/common/utils/default/include/v2_1/GnssTemplate.h b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
index 4d4ec93..79c78c3 100644
--- a/gnss/common/utils/default/include/v2_1/GnssTemplate.h
+++ b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
@@ -28,6 +28,8 @@
#include <string>
#include <thread>
+#include <cutils/properties.h>
+
#include "GnssAntennaInfo.h"
#include "GnssConfiguration.h"
#include "GnssDebug.h"
@@ -157,9 +159,17 @@
std::unique_ptr<V2_0::GnssLocation> GnssTemplate<T_IGnss>::getLocationFromHW() {
char inputBuffer[INPUT_BUFFER_SIZE];
if (!mHardwareModeChecked) {
- mGnssFd = open(GNSS_PATH, O_RDWR | O_NONBLOCK);
+ // default using gnss0
+ const char * gnss_dev_path = GNSS_PATH;
+ char devname_value[PROPERTY_VALUE_MAX] = "";
+ if (property_get("debug.location.gnss.devname", devname_value, NULL) > 0) {
+ gnss_dev_path = devname_value;
+ ALOGD("using %s instead of the default %s", gnss_dev_path, GNSS_PATH);
+ }
+
+ mGnssFd = open(gnss_dev_path, O_RDWR | O_NONBLOCK);
if (mGnssFd == -1) {
- ALOGW("Failed to open /dev/gnss0 errno: %d", errno);
+ ALOGW("Failed to open %s errno: %d", gnss_dev_path, errno);
}
mHardwareModeChecked = true;
}
diff --git a/graphics/composer/2.2/utils/vts/ReadbackVts.cpp b/graphics/composer/2.2/utils/vts/ReadbackVts.cpp
index 7bb9121..b179f35 100644
--- a/graphics/composer/2.2/utils/vts/ReadbackVts.cpp
+++ b/graphics/composer/2.2/utils/vts/ReadbackVts.cpp
@@ -268,7 +268,7 @@
mLayerCount = 1;
mFormat = format;
mUsage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY);
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE);
mAccessRegion.top = 0;
mAccessRegion.left = 0;
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/media/c2/1.2/Android.bp b/media/c2/1.2/Android.bp
new file mode 100644
index 0000000..6d3e74d
--- /dev/null
+++ b/media/c2/1.2/Android.bp
@@ -0,0 +1,40 @@
+// 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",
+ srcs: [
+ "types.hal",
+ "IComponent.hal",
+ "IComponentStore.hal",
+ ],
+ interfaces: [
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ "android.hardware.graphics.common@1.0",
+ "android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.common@1.2",
+ "android.hardware.media.bufferpool@2.0",
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "android.hardware.media.omx@1.0",
+ "android.hardware.media@1.0",
+ "android.hidl.base@1.0",
+ "android.hidl.safe_union@1.0",
+ ],
+ gen_java: false,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ ],
+}
diff --git a/media/c2/1.2/IComponent.hal b/media/c2/1.2/IComponent.hal
new file mode 100644
index 0000000..088d810
--- /dev/null
+++ b/media/c2/1.2/IComponent.hal
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package android.hardware.media.c2@1.2;
+
+import android.hardware.graphics.bufferqueue@2.0::IGraphicBufferProducer;
+import android.hardware.media.c2@1.1::IComponent;
+import android.hardware.media.c2@1.0::Status;
+
+
+/**
+ * Interface for a Codec2 component corresponding to API level 1.2 or below.
+ * Components have two states: stopped and running. The running state has three
+ * sub-states: executing, tripped and error.
+ *
+ * All methods in `IComponent` must not block. If a method call cannot be
+ * completed in a timely manner, it must return `TIMED_OUT` in the return
+ * status.
+ *
+ * @note This is an extension of version 1.1 of `IComponent`. The purpose of the
+ * extension is to add blocking allocation of output buffer from surface.
+ */
+interface IComponent extends @1.1::IComponent {
+ /**
+ * Starts using a surface for output with a synchronization object
+ *
+ * This method must not block.
+ *
+ * @param blockPoolId Id of the `C2BlockPool` to be associated with the
+ * output surface.
+ * @param surface Output surface.
+ * @param syncObject synchronization object for buffer allocation between
+ * Framework and Component.
+ * @return status Status of the call, which may be
+ * - `OK` - The operation completed successfully.
+ * - `CANNOT_DO` - The component does not support an output surface.
+ * - `REFUSED` - The output surface cannot be accessed.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ */
+ setOutputSurfaceWithSyncObj(
+ uint64_t blockPoolId,
+ @2.0::IGraphicBufferProducer surface,
+ SurfaceSyncObj syncObject
+ ) generates (
+ Status status
+ );
+};
diff --git a/media/c2/1.2/IComponentStore.hal b/media/c2/1.2/IComponentStore.hal
new file mode 100644
index 0000000..c38fc7a
--- /dev/null
+++ b/media/c2/1.2/IComponentStore.hal
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package android.hardware.media.c2@1.2;
+
+import android.hardware.media.bufferpool@2.0::IClientManager;
+import android.hardware.media.c2@1.0::IComponentListener;
+import android.hardware.media.c2@1.1::IComponentStore;
+import android.hardware.media.c2@1.0::Status;
+
+import IComponent;
+
+/**
+ * Entry point for Codec2 HAL.
+ *
+ * All methods in `IComponentStore` must not block. If a method call cannot be
+ * completed in a timely manner, it must return `TIMED_OUT` in the return
+ * status. The only exceptions are getPoolClientManager() and getConfigurable(),
+ * which must always return immediately.
+ *
+ * @note This is an extension of version 1.1 of `IComponentStore`. The purpose
+ * of the extension is to add support for blocking output buffer allocator.
+ */
+interface IComponentStore extends @1.1::IComponentStore {
+ /**
+ * Creates a component by name.
+ *
+ * @param name Name of the component to create. This must match one of the
+ * names returned by listComponents().
+ * @param listener Callback receiver.
+ * @param pool `IClientManager` object of the BufferPool in the client
+ * process. This may be null if the client does not own a BufferPool.
+ * @return status Status of the call, which may be
+ * - `OK` - The component was created successfully.
+ * - `NOT_FOUND` - There is no component with the given name.
+ * - `NO_MEMORY` - Not enough memory to create the component.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return comp The created component if @p status is `OK`.
+ *
+ * @sa IComponentListener.
+ */
+ createComponent_1_2(
+ string name,
+ IComponentListener listener,
+ IClientManager pool
+ ) generates (
+ Status status,
+ IComponent comp
+ );
+};
diff --git a/media/c2/1.2/types.hal b/media/c2/1.2/types.hal
new file mode 100644
index 0000000..096edbd
--- /dev/null
+++ b/media/c2/1.2/types.hal
@@ -0,0 +1,52 @@
+/*
+ * 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.media.c2@1.2;
+
+/**
+ * Surface(BufferQueue/IGBP) synchronization object regarding # of dequeued
+ * output buffers. This keeps # of dequeued buffers from Surface less than
+ * configured max # of dequeued buffers all the time.
+ */
+struct SurfaceSyncObj {
+ /**
+ * ASharedMemory for synchronization data. Layout is below
+ *
+ * |lock(futex) 4bytes|
+ * |conditional_variable(futex) 4bytes|
+ * |# of max dequeable buffer 4bytes|
+ * |# of dequeued buffer 4bytes|
+ * |Status of the surface 4bytes|
+ * INIT = 0, Configuring surface is not finished.
+ * ACTIVE = 1, Surface is ready to allocate(dequeue).
+ * SWITCHING = 2, Switching to the new surface. It is blocked
+ * to allocate(dequeue) a buffer until switching
+ * completes.
+ */
+ handle syncMemory;
+
+ // Read-only.
+ // The values which are tied and not changed with respect to Surface
+ // which is currently set up.
+ /** BufferQueue id. */
+ uint64_t bqId;
+ /** Generation id. */
+ uint32_t generationId;
+ /** Consumer usage flags. See +ndk
+ * libnativewindow#AHardwareBuffer_UsageFlags for possible values.
+ */
+ uint64_t consumerUsage;
+};
diff --git a/neuralnetworks/1.0/types.t b/neuralnetworks/1.0/types.t
index d7b26aa..be1ee07 100644
--- a/neuralnetworks/1.0/types.t
+++ b/neuralnetworks/1.0/types.t
@@ -63,361 +63,25 @@
RELU6 = 3,
};
-/**
- * How an operand is used.
- */
-enum OperandLifeTime : int32_t {
- /**
- * The operand is internal to the model. It's created by an operation and
- * consumed by other operations. It must be an output operand of
- * exactly one operation.
- */
- TEMPORARY_VARIABLE,
+%insert OperandLifeTime
- /**
- * The operand is an input of the model. It must not be an output
- * operand of any operation.
- *
- * An operand can't be both input and output of a model.
- */
- MODEL_INPUT,
+%insert DeviceStatus
- /**
- * The operand is an output of the model. It must be an output
- * operand of exactly one operation.
- *
- * An operand can't be both input and output of a model.
- */
- MODEL_OUTPUT,
+%insert PerformanceInfo
- /**
- * The operand is a constant found in Model.operandValues. It must
- * not be an output operand of any operation.
- */
- CONSTANT_COPY,
+%insert Capabilities
- /**
- * The operand is a constant that was specified via a Memory
- * object. It must not be an output operand of any operation.
- */
- CONSTANT_REFERENCE,
+%insert DataLocation
- /**
- * The operand does not have a value. This is valid only for optional
- * arguments of operations.
- */
- NO_VALUE,
-};
+%insert Operand
-/**
- * Status of a device.
- */
-enum DeviceStatus : int32_t {
- AVAILABLE,
- BUSY,
- OFFLINE,
- UNKNOWN,
-};
+%insert Operation
-/**
- * Performance information for the reference workload.
- *
- * Used by a driver to report its performance characteristics.
- */
-struct PerformanceInfo {
- /**
- * Ratio of the time taken by the driver to execute the
- * workload compared to the time the CPU would take for the
- * same workload. A lower number is better.
- */
- float execTime;
+%insert Model
- /**
- * Ratio of the energy used by the driver compared to what
- * the CPU would use for doing the same workload. A lower number
- * is better.
- */
- float powerUsage;
-};
+%insert RequestArgument
-/**
- * The capabilities of a driver.
- */
-struct Capabilities {
- /**
- * Driver performance when operating on float32 data.
- */
- PerformanceInfo float32Performance;
-
- /**
- * Driver performance when operating on asymmetric 8-bit quantized data.
- */
- PerformanceInfo quantized8Performance;
-};
-
-/**
- * Describes the location of a data object.
- */
-struct DataLocation {
- /**
- * The index of the memory pool where this location is found.
- */
- uint32_t poolIndex;
-
- /**
- * Offset in bytes from the start of the pool.
- */
- uint32_t offset;
-
- /**
- * The length of the data in bytes.
- */
- uint32_t length;
-};
-
-/**
- * Describes one operand of the model's graph.
- */
-struct Operand {
- /**
- * Data type of the operand.
- */
- OperandType type;
-
- /**
- * Dimensions of the operand.
- *
- * For a scalar operand, dimensions.size() must be 0.
- *
- * For a tensor operand, dimensions.size() must be at least 1;
- * however, any of the dimensions may be unspecified.
- *
- * A tensor operand with all dimensions specified has "fully
- * specified" dimensions. Whenever possible (i.e., whenever the
- * dimensions are known at model construction time), a tensor
- * operand should have (but is not required to have) fully
- * specified dimensions, in order to enable the best possible
- * performance.
- *
- * If a tensor operand's dimensions are not fully specified, the
- * dimensions of the operand are deduced from the operand
- * dimensions and values of the operation for which that operand
- * is an output.
- *
- * In the following situations, a tensor operand's dimensions must
- * be fully specified:
- *
- * . The operand has lifetime CONSTANT_COPY or
- * CONSTANT_REFERENCE.
- *
- * . The operand has lifetime MODEL_INPUT or MODEL_OUTPUT. Fully
- * specified dimensions must either be present in the
- * Operand or they must be provided in the corresponding
- * RequestArgument.
- * EXCEPTION: If the input or output is optional and omitted
- * (by setting the hasNoValue field of the corresponding
- * RequestArgument to true) then it need not have fully
- * specified dimensions.
- *
- * A tensor operand with some number of unspecified dimensions is
- * represented by setting each unspecified dimension to 0.
- */
- vec<uint32_t> dimensions;
-
- /**
- * The number of times this operand appears as an operation input.
- *
- * (For example, if this operand appears once in one operation's
- * input list, and three times in another operation's input list,
- * then numberOfConsumers = 4.)
- */
- uint32_t numberOfConsumers;
-
- /**
- * Quantized scale of the operand.
- *
- * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
- * TENSOR_INT32.
- */
- float scale;
-
- /**
- * Quantized zero-point offset of the operand.
- *
- * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
- */
- int32_t zeroPoint;
-
- /**
- * How the operand is used.
- */
- OperandLifeTime lifetime;
-
- /**
- * Where to find the data for this operand.
- * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
- * NO_VALUE:
- * - All the fields must be 0.
- * If the lifetime is CONSTANT_COPY:
- * - location.poolIndex is 0.
- * - location.offset is the offset in bytes into Model.operandValues.
- * - location.length is set.
- * If the lifetime is CONSTANT_REFERENCE:
- * - location.poolIndex is set.
- * - location.offset is the offset in bytes into the specified pool.
- * - location.length is set.
- */
- DataLocation location;
-};
-
-/**
- * Describes one operation of the model's graph.
- */
-struct Operation {
- /**
- * The operation type.
- */
- OperationType type;
-
- /**
- * Describes the table that contains the indexes of the inputs of the
- * operation. The offset is the index in the operandIndexes table.
- */
- vec<uint32_t> inputs;
-
- /**
- * Describes the table that contains the indexes of the outputs of the
- * operation. The offset is the index in the operandIndexes table.
- */
- vec<uint32_t> outputs;
-};
-
-/**
- * A Neural Network Model.
- *
- * This includes not only the execution graph, but also constant data such as
- * weights or scalars added at construction time. The only information that
- * might not be known is the shape of the input tensors.
- */
-struct Model {
- /**
- * All operands included in the model.
- */
- vec<Operand> operands;
-
- /**
- * All operations included in the model.
- *
- * The operations are sorted into execution order. Every operand
- * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
- * written before it is read.
- */
- vec<Operation> operations;
-
- /**
- * Input indexes of the model. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> inputIndexes;
-
- /**
- * Output indexes of the model. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> outputIndexes;
-
- /**
- * A byte buffer containing operand data that were copied into the model.
- *
- * An operand's value must be located here if and only if Operand::lifetime
- * equals OperandLifeTime::CONSTANT_COPY.
- */
- vec<uint8_t> operandValues;
-
- /**
- * A collection of shared memory pools containing operand values.
- *
- * An operand's value must be located here if and only if Operand::lifetime
- * equals OperandLifeTime::CONSTANT_REFERENCE.
- */
- vec<memory> pools;
-};
-
-/**
- * Metadata information specifying the location of the input or output data and
- * any updates to the input or output operand.
- */
-struct RequestArgument {
- /**
- * If true, the argument does not have a value. This can be used for
- * operations that take optional arguments. If true, the fields of location
- * are set to 0 and the dimensions vector is left empty.
- */
- bool hasNoValue;
-
- /**
- * The location within one of the memory pools passed in the Request.
- */
- DataLocation location;
-
- /**
- * Updated dimension information.
- *
- * If dimensions.size() > 0, dimension information was provided
- * along with the argument. This can be the case for models that
- * accept inputs of varying size. This can't change the rank, just
- * the value of the dimensions that were unspecified in the
- * model. If dimensions.size() > 0, then all dimensions must be
- * specified here; and any dimension that was specified in the
- * model must have the same value here.
- *
- * If the dimensions in the model are not fully specified, then
- * they must be fully specified here, unless hasNoValue is set to
- * true. If the dimensions in the model are fully specified, then
- * either dimensions.size() may be 0, or the dimensions in the
- * model must be identical to the dimensions here.
- */
- vec<uint32_t> dimensions;
-};
-
-/**
- * Inputs to be sent to and outputs to be retrieved from a prepared model.
- *
- * A Request serves two primary tasks:
- * 1) Provides the input and output data to be used when executing the model.
- * 2) Specifies any updates to the input operand metadata that were left
- * unspecified at model preparation time.
- *
- * An output must not overlap with any other output, with an input, or
- * with an operand of lifetime CONSTANT_REFERENCE.
- */
-struct Request {
- /**
- * Input data and information to be used in the execution of a prepared
- * model.
- *
- * The index of the input corresponds to the index in Model.inputIndexes.
- * E.g., input[i] corresponds to Model.inputIndexes[i].
- */
- vec<RequestArgument> inputs;
-
- /**
- * Output data and information to be used in the execution of a prepared
- * model.
- *
- * The index of the output corresponds to the index in Model.outputIndexes.
- * E.g., output[i] corresponds to Model.outputIndexes[i].
- */
- vec<RequestArgument> outputs;
-
- /**
- * A collection of shared memory pools containing operand data for both the
- * inputs and the outputs to a model.
- */
- vec<memory> pools;
-};
+%insert Request
/**
* Return status of a function.
diff --git a/neuralnetworks/1.1/types.t b/neuralnetworks/1.1/types.t
index 75ac2e7..8c22b30 100644
--- a/neuralnetworks/1.1/types.t
+++ b/neuralnetworks/1.1/types.t
@@ -31,128 +31,10 @@
%insert Operation_1.1
};
-/**
- * The capabilities of a driver.
- */
-struct Capabilities {
- /**
- * Driver performance when operating on float32 data.
- */
- PerformanceInfo float32Performance;
+%insert Capabilities
- /**
- * Driver performance when operating on asymmetric 8-bit quantized data.
- */
- PerformanceInfo quantized8Performance;
+%insert Operation
- /**
- * Driver performance when operating on float32 data but performing
- * calculations with range and/or precision as low as that of the IEEE
- * 754 16-bit floating-point format.
- */
- PerformanceInfo relaxedFloat32toFloat16Performance;
-};
+%insert Model
-/**
- * Describes one operation of the model's graph.
- */
-struct Operation {
- /**
- * The operation type.
- */
- OperationType type;
-
- /**
- * Describes the table that contains the indexes of the inputs of the
- * operation. The offset is the index in the operandIndexes table.
- */
- vec<uint32_t> inputs;
-
- /**
- * Describes the table that contains the indexes of the outputs of the
- * operation. The offset is the index in the operandIndexes table.
- */
- vec<uint32_t> outputs;
-};
-
-/**
- * A Neural Network Model.
- *
- * This includes not only the execution graph, but also constant data such as
- * weights or scalars added at construction time. The only information that
- * may not be known is the shape of the input tensors.
- */
-struct Model {
- /**
- * All operands included in the model.
- */
- vec<Operand> operands;
-
- /**
- * All operations included in the model.
- *
- * The operations are sorted into execution order. Every operand
- * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
- * written before it is read.
- */
- vec<Operation> operations;
-
- /**
- * Input indexes of the model. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> inputIndexes;
-
- /**
- * Output indexes of the model. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> outputIndexes;
-
- /**
- * A byte buffer containing operand data that were copied into the model.
- *
- * An operand's value must be located here if and only if Operand::lifetime
- * equals OperandLifeTime::CONSTANT_COPY.
- */
- vec<uint8_t> operandValues;
-
- /**
- * A collection of shared memory pools containing operand values.
- *
- * An operand's value must be located here if and only if Operand::lifetime
- * equals OperandLifeTime::CONSTANT_REFERENCE.
- */
- vec<memory> pools;
-
- /**
- * 'true' indicates TENSOR_FLOAT32 may be calculated with range and/or
- * precision as low as that of the IEEE 754 16-bit floating-point format.
- * 'false' indicates TENSOR_FLOAT32 must be calculated using at least the
- * range and precision of the IEEE 754 32-bit floating-point format.
- */
- bool relaxComputationFloat32toFloat16;
-};
-
-/**
- * Execution preferences.
- */
-enum ExecutionPreference : int32_t {
- /**
- * Prefer executing in a way that minimizes battery drain.
- * This is desirable for compilations that will be executed often.
- */
- LOW_POWER = 0,
- /**
- * Prefer returning a single answer as fast as possible, even if this causes
- * more power consumption.
- */
- FAST_SINGLE_ANSWER = 1,
- /**
- * Prefer maximizing the throughput of successive frames, for example when
- * processing successive frames coming from the camera.
- */
- SUSTAINED_SPEED = 2,
-};
+%insert ExecutionPreference
diff --git a/neuralnetworks/1.2/types.t b/neuralnetworks/1.2/types.t
index 4c9fd02..b490f7f 100644
--- a/neuralnetworks/1.2/types.t
+++ b/neuralnetworks/1.2/types.t
@@ -97,379 +97,23 @@
BASE_MAX = 0xFFFF,
};
-/**
- * Device types.
- *
- * The type of NNAPI device.
- */
-enum DeviceType : int32_t {
- // Leaving 0 unused as it means unknown type in NDK NNAPI. There is no
- // HAL equivalent of unknown type and a 1.2 HAL implementation must belong
- // to one of the categories below.
- /** The device does not fall into any category below. */
- OTHER = 1,
- /** The device runs NNAPI models on single or multi-core CPU. */
- CPU = 2,
- /** The device can run NNAPI models and also accelerate graphics APIs such
- * as OpenGL ES and Vulkan. */
- GPU = 3,
- /** Dedicated accelerator for Machine Learning workloads. */
- ACCELERATOR = 4,
-};
+%insert DeviceType
-/**
- * The capabilities of a driver.
- *
- * Performance of an operation comes from the type of its first operand.
- * This represents performance for non extension operand types.
- */
-struct Capabilities {
- /**
- * Driver performance when operating on float32 data but performing
- * calculations with range and/or precision as low as that of the IEEE
- * 754 16-bit floating-point format.
- */
- PerformanceInfo relaxedFloat32toFloat16PerformanceScalar;
- PerformanceInfo relaxedFloat32toFloat16PerformanceTensor;
+%insert Capabilities
- /**
- * Driver performance when operating on a particular data type.
- * In the case of float32 data, this is used when the calculations
- * are not relaxed.
- */
- struct OperandPerformance {
- OperandType type;
- PerformanceInfo info;
- };
+%insert Operation
- /**
- * Performance by operand type. Must be sorted by OperandType.
- * If a particular OperandType is not present in operandPerformance,
- * its performance is treated as { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
- */
- vec<OperandPerformance> operandPerformance;
-};
+%insert SymmPerChannelQuantParams
-/**
- * Describes one operation of the model's graph.
- */
-struct Operation {
- /**
- * The operation type.
- *
- * Besides the values listed in {@link OperationType}, any value above
- * {@link OperationTypeRange::BASE_MAX} is possible and should be interpreted
- * as an extension type according to {@link Model::extensionNameToPrefix}.
- */
- OperationType type;
+%insert Operand
- /**
- * Describes the table that contains the indexes of the inputs of the
- * operation. The offset is the index in the operandIndexes table.
- */
- vec<uint32_t> inputs;
+%insert Model
- /**
- * Describes the table that contains the indexes of the outputs of the
- * operation. The offset is the index in the operandIndexes table.
- */
- vec<uint32_t> outputs;
-};
+%insert OutputShape
-/**
- * Parameters for TENSOR_QUANT8_SYMM_PER_CHANNEL operand.
- */
-struct SymmPerChannelQuantParams {
- /** Array of scaling values for each channel. Each value must be greater than zero. */
- vec<float> scales;
- /** Index of the channel dimension */
- uint32_t channelDim;
-};
+%insert MeasureTiming
-/**
- * Describes one operand of the model's graph.
- */
-struct Operand {
- /**
- * The data type.
- *
- * Besides the values listed in {@link OperandType}, any value above
- * {@link OperandTypeRange::BASE_MAX} is possible and should be interpreted
- * as an extension type according to {@link Model::extensionNameToPrefix}.
- */
- OperandType type;
-
- /**
- * Dimensions of the operand.
- *
- * For a scalar operand, dimensions.size() must be 0.
- *
- * A tensor operand with all dimensions specified has "fully
- * specified" dimensions. Whenever possible (i.e., whenever the
- * dimensions are known at model construction time), a tensor
- * operand should have (but is not required to have) fully
- * specified dimensions, in order to enable the best possible
- * performance.
- *
- * If a tensor operand's dimensions are not fully specified, the
- * dimensions of the operand are deduced from the operand
- * dimensions and values of the operation for which that operand
- * is an output.
- *
- * In the following situations, a tensor operand's dimensions must
- * be fully specified:
- *
- * . The operand has lifetime CONSTANT_COPY or
- * CONSTANT_REFERENCE.
- *
- * . The operand has lifetime MODEL_INPUT. Fully
- * specified dimensions must either be present in the
- * Operand or they must be provided in the corresponding
- * RequestArgument.
- * EXCEPTION: If the input is optional and omitted
- * (by setting the hasNoValue field of the corresponding
- * RequestArgument to true) then it need not have fully
- * specified dimensions.
- *
- * A tensor operand with some number of unspecified dimensions is
- * represented by setting each unspecified dimension to 0.
- *
- * A tensor operand with unspecified rank is represented by providing
- * an empty dimensions vector.
- */
- vec<uint32_t> dimensions;
-
- /**
- * The number of times this operand appears as an operation input.
- *
- * (For example, if this operand appears once in one operation's
- * input list, and three times in another operation's input list,
- * then numberOfConsumers = 4.)
- */
- uint32_t numberOfConsumers;
-
- /**
- * Quantized scale of the operand.
- *
- * Must be 0 when not applicable to an operand type.
- *
- * See {@link OperandType}.
- */
- float scale;
-
- /**
- * Quantized zero-point offset of the operand.
- *
- * Must be 0 when not applicable to an operand type.
- *
- * See {@link OperandType}.
- */
- int32_t zeroPoint;
-
- /**
- * How the operand is used.
- */
- OperandLifeTime lifetime;
-
- /**
- * Where to find the data for this operand.
- * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
- * NO_VALUE:
- * - All the fields must be 0.
- * If the lifetime is CONSTANT_COPY:
- * - location.poolIndex is 0.
- * - location.offset is the offset in bytes into Model.operandValues.
- * - location.length is set.
- * If the lifetime is CONSTANT_REFERENCE:
- * - location.poolIndex is set.
- * - location.offset is the offset in bytes into the specified pool.
- * - location.length is set.
- */
- DataLocation location;
-
- /**
- * Additional parameters specific to a particular operand type.
- */
- safe_union ExtraParams {
- /**
- * No additional parameters.
- */
- Monostate none;
-
- /**
- * Symmetric per-channel quantization parameters.
- *
- * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
- */
- SymmPerChannelQuantParams channelQuant;
-
- /**
- * Extension operand parameters.
- *
- * The framework treats this as an opaque data blob.
- * The format is up to individual extensions.
- */
- vec<uint8_t> extension;
- } extraParams;
-};
-
-/**
- * A Neural Network Model.
- *
- * This includes not only the execution graph, but also constant data such as
- * weights or scalars added at construction time. The only information that
- * may not be known is the shape of the input tensors.
- */
-struct Model {
- /**
- * All operands included in the model.
- */
- vec<Operand> operands;
-
- /**
- * All operations included in the model.
- *
- * The operations are sorted into execution order. Every operand
- * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
- * written before it is read.
- */
- vec<Operation> operations;
-
- /**
- * Input indexes of the model. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> inputIndexes;
-
- /**
- * Output indexes of the model. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> outputIndexes;
-
- /**
- * A byte buffer containing operand data that were copied into the model.
- *
- * An operand's value must be located here if and only if Operand::lifetime
- * equals OperandLifeTime::CONSTANT_COPY.
- */
- vec<uint8_t> operandValues;
-
- /**
- * A collection of shared memory pools containing operand values.
- *
- * An operand's value must be located here if and only if Operand::lifetime
- * equals OperandLifeTime::CONSTANT_REFERENCE.
- */
- vec<memory> pools;
-
- /**
- * 'true' indicates TENSOR_FLOAT32 may be calculated with range and/or
- * precision as low as that of the IEEE 754 16-bit floating-point format.
- * 'false' indicates TENSOR_FLOAT32 must be calculated using at least the
- * range and precision of the IEEE 754 32-bit floating-point format.
- */
- bool relaxComputationFloat32toFloat16;
-
- /**
- * The mapping between extension names and prefixes of operand and
- * operation type values.
- *
- * An operand or operation whose numeric type value is above
- * {@link OperandTypeRange::BASE_MAX} or
- * {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
- * as an extension operand. The low
- * {@link Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the value
- * correspond to the type ID within the extension and the high
- * {@link Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
- * the "prefix", which maps uniquely to the extension name.
- *
- * For example, if a model contains an operation whose value is
- * 0xAAAABBBB and extensionNameToPrefix contains an entry with
- * prefix=0xAAAA and name="vendor.test.test_extension", then
- * the operation should be interpreted as the operation 0xBBBB
- * of the extension named vendor.test.test_extension.
- *
- * This is a one-to-one correspondence. That is, there must be at most one
- * prefix corresponding to each extension name and at most one extension
- * name corresponding to each prefix.
- */
- vec<ExtensionNameAndPrefix> extensionNameToPrefix;
-
- /**
- * A correspondence between an extension name and a prefix of operand and
- * operation type values.
- */
- struct ExtensionNameAndPrefix {
- /**
- * The extension name.
- *
- * See {@link Extension::name} for the format specification.
- */
- string name;
-
- /**
- * The unique extension identifier within the model.
- *
- * See {@link Model::extensionNameToPrefix}.
- */
- uint16_t prefix;
- };
-
- /**
- * Numeric values of extension operand and operation types have the
- * following structure:
- * - 16 high bits represent the "prefix", which corresponds uniquely to the
- * extension name.
- * - 16 low bits represent the type ID within the extension.
- */
- enum ExtensionTypeEncoding : uint8_t {
- HIGH_BITS_PREFIX = 16,
- LOW_BITS_TYPE = 16,
- };
-};
-
-/**
- * Describes the shape information of an output operand after execution.
- */
-struct OutputShape {
- /**
- * Dimensions of the operand.
- */
- vec<uint32_t> dimensions;
-
- /**
- * Whether the provided buffer size is sufficient for the output.
- */
- bool isSufficient;
-};
-
-/**
- * Specifies whether or not to measure timing information during execution.
- */
-enum MeasureTiming : int32_t {
- NO = 0,
- YES = 1,
-};
-
-/**
-
- * Timing information measured during execution. Each time is a duration from
- * the beginning of some task to the end of that task, including time when that
- * task is not active (for example, preempted by some other task, or
- * waiting for some resource to become available).
- *
- * Times are measured in microseconds.
- * When a time is not available, it must be reported as UINT64_MAX.
- */
-struct Timing {
- /** Execution time on device (not driver, which runs on host processor). */
- uint64_t timeOnDevice;
- /** Execution time in driver (including time on device). */
- uint64_t timeInDriver;
-};
+%insert Timing
/**
* FmqRequestDatum is a single element of a serialized representation of an
@@ -683,46 +327,4 @@
Timing executionTiming;
};
-/**
- * Information about an extension.
- */
-struct Extension {
- /**
- * The extension name.
- *
- * The name must consist of lowercase latin letters, numbers, periods, and
- * underscore signs. The name must contain at least one period.
- *
- * The name must start with the reverse domain name of the vendor.
- *
- * Example: com.google.test_extension
- */
- string name;
-
- /**
- * Information about an extension operand type.
- */
- struct OperandTypeInformation {
- /**
- * The extension operand type.
- */
- uint16_t type;
-
- /**
- * Indicates whether the extension operand type represents a tensor or
- * a scalar.
- */
- bool isTensor;
-
- /**
- * The byte size of the operand (if scalar) or of a single element (if
- * tensor).
- */
- uint32_t byteSize;
- };
-
- /**
- * Information about operand types defined by the extension.
- */
- vec<OperandTypeInformation> operandTypes;
-};
+%insert Extension
diff --git a/neuralnetworks/1.2/utils/Android.bp b/neuralnetworks/1.2/utils/Android.bp
index 2921141..41281ee 100644
--- a/neuralnetworks/1.2/utils/Android.bp
+++ b/neuralnetworks/1.2/utils/Android.bp
@@ -27,7 +27,6 @@
name: "neuralnetworks_utils_hal_1_2",
defaults: ["neuralnetworks_utils_defaults"],
srcs: ["src/*"],
- exclude_srcs: ["src/ExecutionBurst*"],
local_include_dirs: ["include/nnapi/hal/1.2/"],
export_include_dirs: ["include"],
cflags: ["-Wthread-safety"],
@@ -41,10 +40,16 @@
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
+ "libfmq",
],
export_static_lib_headers: [
"neuralnetworks_utils_hal_common",
],
+ product_variables: {
+ debuggable: { // eng and userdebug builds
+ cflags: ["-DNN_DEBUGGABLE"],
+ },
+ },
}
cc_test {
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
index 6fd1337..272cee7 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
@@ -52,6 +52,7 @@
GeneralResult<Model> convert(const hal::V1_2::Model& model);
GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming);
GeneralResult<Timing> convert(const hal::V1_2::Timing& timing);
+GeneralResult<SharedMemory> convert(const hardware::hidl_memory& memory);
GeneralResult<std::vector<Extension>> convert(
const hardware::hidl_vec<hal::V1_2::Extension>& extensions);
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 5356a91..6b6fc71 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
@@ -14,23 +14,28 @@
* limitations under the License.
*/
-#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
-#define ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
#include "ExecutionBurstUtils.h"
-#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
-#include <android/hardware/neuralnetworks/1.1/types.h>
#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ProtectCallback.h>
#include <atomic>
#include <chrono>
+#include <functional>
#include <map>
#include <memory>
#include <mutex>
@@ -39,147 +44,145 @@
#include <utility>
#include <vector>
-namespace android::nn {
+namespace android::hardware::neuralnetworks::V1_2::utils {
/**
- * The ExecutionBurstController class manages both the serialization and
- * deserialization of data across FMQ, making it appear to the runtime as a
- * regular synchronous inference. Additionally, this class manages the burst's
- * memory cache.
+ * The ExecutionBurstController class manages both the serialization and deserialization of data
+ * across FMQ, making it appear to the runtime as a regular synchronous inference. Additionally,
+ * this class manages the burst's memory cache.
*/
-class ExecutionBurstController {
- DISALLOW_IMPLICIT_CONSTRUCTORS(ExecutionBurstController);
+class ExecutionBurstController final : public nn::IBurst {
+ struct PrivateConstructorTag {};
public:
+ using FallbackFunction =
+ std::function<nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>(
+ const nn::Request&, nn::MeasureTiming)>;
+
/**
- * NN runtime burst callback object and memory cache.
+ * NN runtime memory cache.
*
- * ExecutionBurstCallback associates a hidl_memory object with a slot number
- * to be passed across FMQ. The ExecutionBurstServer can use this callback
- * to retrieve this hidl_memory corresponding to the slot via HIDL.
+ * MemoryCache associates a Memory object with a slot number to be passed across FMQ. The
+ * ExecutionBurstServer can use this callback to retrieve a hidl_memory corresponding to the
+ * slot via HIDL.
*
- * Whenever a hidl_memory object is copied, it will duplicate the underlying
- * file descriptor. Because the NN runtime currently copies the hidl_memory
- * on each execution, it is difficult to associate hidl_memory objects with
- * previously cached hidl_memory objects. For this reason, callers of this
- * class must pair each hidl_memory object with an associated key. For
- * efficiency, if two hidl_memory objects represent the same underlying
- * buffer, they must use the same key.
+ * Whenever a hidl_memory object is copied, it will duplicate the underlying file descriptor.
+ * Because the NN runtime currently copies the hidl_memory on each execution, it is difficult to
+ * associate hidl_memory objects with previously cached hidl_memory objects. For this reason,
+ * callers of this class must pair each hidl_memory object with an associated key. For
+ * efficiency, if two hidl_memory objects represent the same underlying buffer, they must use
+ * the same key.
+ *
+ * This class is thread-safe.
*/
- class ExecutionBurstCallback : public hardware::neuralnetworks::V1_2::IBurstCallback {
- DISALLOW_COPY_AND_ASSIGN(ExecutionBurstCallback);
+ class MemoryCache : public std::enable_shared_from_this<MemoryCache> {
+ struct PrivateConstructorTag {};
public:
- ExecutionBurstCallback() = default;
+ using Task = std::function<void()>;
+ using Cleanup = base::ScopeGuard<Task>;
+ using SharedCleanup = std::shared_ptr<const Cleanup>;
+ using WeakCleanup = std::weak_ptr<const Cleanup>;
- hardware::Return<void> getMemories(const hardware::hidl_vec<int32_t>& slots,
- getMemories_cb cb) override;
+ // Custom constructor to pre-allocate cache sizes.
+ MemoryCache();
/**
- * This function performs one of two different actions:
- * 1) If a key corresponding to a memory resource is unrecognized by the
- * ExecutionBurstCallback object, the ExecutionBurstCallback object
- * will allocate a slot, bind the memory to the slot, and return the
- * slot identifier.
- * 2) If a key corresponding to a memory resource is recognized by the
- * ExecutionBurstCallback object, the ExecutionBurstCallback object
- * will return the existing slot identifier.
+ * Add a burst context to the MemoryCache object.
*
- * @param memories Memory resources used in an inference.
- * @param keys Unique identifiers where each element corresponds to a
- * memory resource element in "memories".
- * @return Unique slot identifiers where each returned slot element
- * corresponds to a memory resource element in "memories".
+ * If this method is called, it must be called before the MemoryCache::cacheMemory or
+ * MemoryCache::getMemory is used.
+ *
+ * @param burstContext Burst context to be added to the MemoryCache object.
*/
- std::vector<int32_t> getSlots(const hardware::hidl_vec<hardware::hidl_memory>& memories,
- const std::vector<intptr_t>& keys);
+ void setBurstContext(sp<IBurstContext> burstContext);
- /*
- * This function performs two different actions:
- * 1) Removes an entry from the cache (if present), including the local
- * storage of the hidl_memory object. Note that this call does not
- * free any corresponding hidl_memory object in ExecutionBurstServer,
- * which is separately freed via IBurstContext::freeMemory.
- * 2) Return whether a cache entry was removed and which slot was removed if
- * found. If the key did not to correspond to any entry in the cache, a
- * slot number of 0 is returned. The slot number and whether the entry
- * existed is useful so the same slot can be freed in the
- * ExecutionBurstServer's cache via IBurstContext::freeMemory.
+ /**
+ * 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<bool, int32_t> freeMemory(intptr_t key);
+ std::pair<int32_t, SharedCleanup> cacheMemory(const nn::SharedMemory& memory);
+
+ /**
+ * Get the memory object corresponding to a slot identifier.
+ *
+ * @param slot Slot which identifies the memory object to retrieve.
+ * @return The memory object corresponding to slot, otherwise GeneralError.
+ */
+ nn::GeneralResult<nn::SharedMemory> getMemory(int32_t slot);
private:
- int32_t getSlotLocked(const hardware::hidl_memory& memory, intptr_t key);
- int32_t allocateSlotLocked();
+ void freeMemory(const nn::SharedMemory& memory);
+ int32_t allocateSlotLocked() REQUIRES(mMutex);
std::mutex mMutex;
- std::stack<int32_t, std::vector<int32_t>> mFreeSlots;
- std::map<intptr_t, int32_t> mMemoryIdToSlot;
- std::vector<hardware::hidl_memory> mMemoryCache;
+ std::condition_variable mCond;
+ sp<IBurstContext> mBurstContext GUARDED_BY(mMutex);
+ std::stack<int32_t, std::vector<int32_t>> mFreeSlots GUARDED_BY(mMutex);
+ std::map<nn::SharedMemory, int32_t> mMemoryIdToSlot GUARDED_BY(mMutex);
+ std::vector<nn::SharedMemory> mMemoryCache GUARDED_BY(mMutex);
+ std::vector<WeakCleanup> mCacheCleaner GUARDED_BY(mMutex);
+ };
+
+ /**
+ * HIDL Callback class to pass memory objects to the Burst server when given corresponding
+ * slots.
+ */
+ class ExecutionBurstCallback : public IBurstCallback {
+ public:
+ // Precondition: memoryCache must be non-null.
+ explicit ExecutionBurstCallback(const std::shared_ptr<MemoryCache>& memoryCache);
+
+ // See IBurstCallback::getMemories for information on this method.
+ Return<void> getMemories(const hidl_vec<int32_t>& slots, getMemories_cb cb) override;
+
+ private:
+ const std::weak_ptr<MemoryCache> kMemoryCache;
};
/**
* Creates a burst controller on a prepared model.
*
- * Prefer this over ExecutionBurstController's constructor.
- *
* @param preparedModel Model prepared for execution to execute on.
- * @param pollingTimeWindow How much time (in microseconds) the
- * ExecutionBurstController is allowed to poll the FMQ before waiting on
- * the blocking futex. Polling may result in lower latencies at the
- * potential cost of more power usage.
+ * @param pollingTimeWindow How much time (in microseconds) the ExecutionBurstController is
+ * allowed to poll the FMQ before waiting on the blocking futex. Polling may result in lower
+ * latencies at the potential cost of more power usage.
* @return ExecutionBurstController Execution burst controller object.
*/
- static std::unique_ptr<ExecutionBurstController> create(
- const sp<hardware::neuralnetworks::V1_2::IPreparedModel>& preparedModel,
+ static nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> create(
+ const sp<IPreparedModel>& preparedModel, FallbackFunction fallback,
std::chrono::microseconds pollingTimeWindow);
- // prefer calling ExecutionBurstController::create
- ExecutionBurstController(const std::shared_ptr<RequestChannelSender>& requestChannelSender,
- const std::shared_ptr<ResultChannelReceiver>& resultChannelReceiver,
- const sp<hardware::neuralnetworks::V1_2::IBurstContext>& burstContext,
- const sp<ExecutionBurstCallback>& callback,
- const sp<hardware::hidl_death_recipient>& deathHandler = nullptr);
+ ExecutionBurstController(PrivateConstructorTag tag, FallbackFunction fallback,
+ std::unique_ptr<RequestChannelSender> requestChannelSender,
+ std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
+ sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
+ std::shared_ptr<MemoryCache> memoryCache,
+ neuralnetworks::utils::DeathHandler deathHandler);
- // explicit destructor to unregister the death recipient
- ~ExecutionBurstController();
+ // See IBurst::cacheMemory for information on this method.
+ OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
- /**
- * Execute a request on a model.
- *
- * @param request Arguments to be executed on a model.
- * @param measure Whether to collect timing measurements, either YES or NO
- * @param memoryIds Identifiers corresponding to each memory object in the
- * request's pools.
- * @return A tuple of:
- * - result code of the execution
- * - dynamic output shapes from the execution
- * - any execution time measurements of the execution
- * - whether or not a failed burst execution should be re-run using a
- * different path (e.g., IPreparedModel::executeSynchronously)
- */
- std::tuple<int, std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
- hardware::neuralnetworks::V1_2::Timing, bool>
- compute(const hardware::neuralnetworks::V1_0::Request& request,
- hardware::neuralnetworks::V1_2::MeasureTiming measure,
- const std::vector<intptr_t>& memoryIds);
-
- /**
- * Propagate a user's freeing of memory to the service.
- *
- * @param key Key corresponding to the memory object.
- */
- void freeMemory(intptr_t key);
+ // 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;
private:
- std::mutex mMutex;
- const std::shared_ptr<RequestChannelSender> mRequestChannelSender;
- const std::shared_ptr<ResultChannelReceiver> mResultChannelReceiver;
- const sp<hardware::neuralnetworks::V1_2::IBurstContext> mBurstContext;
- const sp<ExecutionBurstCallback> mMemoryCache;
- const sp<hardware::hidl_death_recipient> mDeathHandler;
+ mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
+ const FallbackFunction kFallback;
+ const std::unique_ptr<RequestChannelSender> mRequestChannelSender;
+ const std::unique_ptr<ResultChannelReceiver> mResultChannelReceiver;
+ const sp<ExecutionBurstCallback> mBurstCallback;
+ const sp<IBurstContext> mBurstContext;
+ const std::shared_ptr<MemoryCache> mMemoryCache;
+ // `kDeathHandler` must come after `mRequestChannelSender` and `mResultChannelReceiver` because
+ // it holds references to both objects.
+ const neuralnetworks::utils::DeathHandler kDeathHandler;
};
-} // namespace android::nn
+} // namespace android::hardware::neuralnetworks::V1_2::utils
-#endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
index 2e109b2..f7926f5 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
@@ -14,19 +14,22 @@
* limitations under the License.
*/
-#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
-#define ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_SERVER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_SERVER_H
#include "ExecutionBurstUtils.h"
-#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
-#include <android/hardware/neuralnetworks/1.1/types.h>
#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ProtectCallback.h>
#include <atomic>
#include <chrono>
@@ -36,84 +39,61 @@
#include <tuple>
#include <vector>
-namespace android::nn {
+namespace android::hardware::neuralnetworks::V1_2::utils {
/**
- * The ExecutionBurstServer class is responsible for waiting for and
- * deserializing a request object from a FMQ, performing the inference, and
- * serializing the result back across another FMQ.
+ * The ExecutionBurstServer class is responsible for waiting for and deserializing a request object
+ * from a FMQ, performing the inference, and serializing the result back across another FMQ.
*/
-class ExecutionBurstServer : public hardware::neuralnetworks::V1_2::IBurstContext {
- DISALLOW_IMPLICIT_CONSTRUCTORS(ExecutionBurstServer);
+class ExecutionBurstServer : public IBurstContext {
+ struct PrivateConstructorTag {};
public:
/**
- * IBurstExecutorWithCache is a callback object passed to
- * ExecutionBurstServer's factory function that is used to perform an
- * execution. Because some memory resources are needed across multiple
- * executions, this object also contains a local cache that can directly be
- * used in the execution.
+ * Class to cache the memory objects for a burst object.
*
- * ExecutionBurstServer will never access its IBurstExecutorWithCache object
- * with concurrent calls.
+ * This class is thread-safe.
*/
- class IBurstExecutorWithCache {
- DISALLOW_COPY_AND_ASSIGN(IBurstExecutorWithCache);
-
+ class MemoryCache {
public:
- IBurstExecutorWithCache() = default;
- virtual ~IBurstExecutorWithCache() = default;
+ // Precondition: burstExecutor != nullptr
+ // Precondition: burstCallback != nullptr
+ MemoryCache(nn::SharedBurst burstExecutor, sp<IBurstCallback> burstCallback);
/**
- * Checks if a cache entry specified by a slot is present in the cache.
+ * Get the cached memory objects corresponding to provided slot identifiers.
*
- * @param slot Identifier of the cache entry.
- * @return 'true' if the cache entry is present in the cache, 'false'
- * otherwise.
+ * If the slot entry is not present in the cache, this class will use IBurstCallback to
+ * retrieve those entries that are not present in the cache, then cache them.
+ *
+ * @param slots Identifiers of memory objects to be retrieved.
+ * @return A vector where each element is the memory object and a ref-counted cache "hold"
+ * object to preserve the cache entry of the IBurst object as long as the "hold" object
+ * is alive, otherwise GeneralError. Each element of the vector corresponds to the
+ * element of slot.
*/
- virtual bool isCacheEntryPresent(int32_t slot) const = 0;
+ nn::GeneralResult<std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>>
+ getCacheEntries(const std::vector<int32_t>& slots);
/**
- * Adds an entry specified by a slot to the cache.
+ * Remove an entry from the cache.
*
- * The caller of this function must ensure that the cache entry that is
- * being added is not already present in the cache. This can be checked
- * via isCacheEntryPresent.
- *
- * @param memory Memory resource to be cached.
- * @param slot Slot identifier corresponding to the memory resource.
+ * @param slot Identifier of the memory object to be removed from the cache.
*/
- virtual void addCacheEntry(const hardware::hidl_memory& memory, int32_t slot) = 0;
+ void removeCacheEntry(int32_t slot);
- /**
- * Removes an entry specified by a slot from the cache.
- *
- * If the cache entry corresponding to the slot number does not exist,
- * the call does nothing.
- *
- * @param slot Slot identifier corresponding to the memory resource.
- */
- virtual void removeCacheEntry(int32_t slot) = 0;
+ private:
+ nn::GeneralResult<void> ensureCacheEntriesArePresentLocked(
+ const std::vector<int32_t>& slots) REQUIRES(mMutex);
+ nn::GeneralResult<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>
+ getCacheEntryLocked(int32_t slot) REQUIRES(mMutex);
+ void addCacheEntryLocked(int32_t slot, nn::SharedMemory memory) REQUIRES(mMutex);
- /**
- * Perform an execution.
- *
- * @param request Request object with inputs and outputs specified.
- * Request::pools is empty, and DataLocation::poolIndex instead
- * refers to the 'slots' argument as if it were Request::pools.
- * @param slots Slots corresponding to the cached memory entries to be
- * used.
- * @param measure Whether timing information is requested for the
- * execution.
- * @return Result of the execution, including the status of the
- * execution, dynamic output shapes, and any timing information.
- */
- virtual std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
- hardware::hidl_vec<hardware::neuralnetworks::V1_2::OutputShape>,
- hardware::neuralnetworks::V1_2::Timing>
- execute(const hardware::neuralnetworks::V1_0::Request& request,
- const std::vector<int32_t>& slots,
- hardware::neuralnetworks::V1_2::MeasureTiming measure) = 0;
+ std::mutex mMutex;
+ std::map<int32_t, std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>> mCache
+ GUARDED_BY(mMutex);
+ nn::SharedBurst kBurstExecutor;
+ const sp<IBurstCallback> kBurstCallback;
};
/**
@@ -124,85 +104,52 @@
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
- * @param callback Callback used to retrieve memories corresponding to
- * unrecognized slots.
- * @param requestChannel Input FMQ channel through which the client passes the
- * request to the service.
- * @param resultChannel Output FMQ channel from which the client can retrieve
- * the result of the execution.
- * @param executorWithCache Object which maintains a local cache of the
- * memory pools and executes using the cached memory pools.
- * @param pollingTimeWindow How much time (in microseconds) the
- * ExecutionBurstServer is allowed to poll the FMQ before waiting on
- * the blocking futex. Polling may result in lower latencies at the
- * potential cost of more power usage.
- * @result IBurstContext Handle to the burst context.
- */
- static sp<ExecutionBurstServer> create(
- const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
- const FmqRequestDescriptor& requestChannel, const FmqResultDescriptor& resultChannel,
- std::shared_ptr<IBurstExecutorWithCache> executorWithCache,
- std::chrono::microseconds pollingTimeWindow = std::chrono::microseconds{0});
-
- /**
- * Create automated context to manage FMQ-based executions.
- *
- * This function is intended to be used by a service to automatically:
- * 1) Receive data from a provided FMQ
- * 2) Execute a model with the given information
- * 3) Send the result to the created FMQ
- *
- * @param callback Callback used to retrieve memories corresponding to
- * unrecognized slots.
- * @param requestChannel Input FMQ channel through which the client passes the
- * request to the service.
- * @param resultChannel Output FMQ channel from which the client can retrieve
- * the result of the execution.
- * @param preparedModel PreparedModel that the burst object was created from.
- * IPreparedModel::executeSynchronously will be used to perform the
+ * @param callback Callback used to retrieve memories corresponding to unrecognized slots.
+ * @param requestChannel Input FMQ channel through which the client passes the request to the
+ * service.
+ * @param resultChannel Output FMQ channel from which the client can retrieve the result of the
* execution.
- * @param pollingTimeWindow How much time (in microseconds) the
- * ExecutionBurstServer is allowed to poll the FMQ before waiting on
- * the blocking futex. Polling may result in lower latencies at the
- * potential cost of more power usage.
- * @result IBurstContext Handle to the burst context.
+ * @param burstExecutor Object which maintains a local cache of the memory pools and executes
+ * using the cached memory pools.
+ * @param pollingTimeWindow How much time (in microseconds) the ExecutionBurstServer is allowed
+ * to poll the FMQ before waiting on the blocking futex. Polling may result in lower
+ * latencies at the potential cost of more power usage.
+ * @return IBurstContext Handle to the burst context.
*/
- static sp<ExecutionBurstServer> create(
- const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
- const FmqRequestDescriptor& requestChannel, const FmqResultDescriptor& resultChannel,
- hardware::neuralnetworks::V1_2::IPreparedModel* preparedModel,
+ static nn::GeneralResult<sp<ExecutionBurstServer>> create(
+ const sp<IBurstCallback>& callback,
+ const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
std::chrono::microseconds pollingTimeWindow = std::chrono::microseconds{0});
- ExecutionBurstServer(const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
+ ExecutionBurstServer(PrivateConstructorTag tag, const sp<IBurstCallback>& callback,
std::unique_ptr<RequestChannelReceiver> requestChannel,
std::unique_ptr<ResultChannelSender> resultChannel,
- std::shared_ptr<IBurstExecutorWithCache> cachedExecutor);
+ nn::SharedBurst burstExecutor);
~ExecutionBurstServer();
- // Used by the NN runtime to preemptively remove any stored memory.
- hardware::Return<void> freeMemory(int32_t slot) override;
+ // Used by the NN runtime to preemptively remove any stored memory. See
+ // IBurstContext::freeMemory for more information.
+ Return<void> freeMemory(int32_t slot) override;
private:
- // Ensures all cache entries contained in mExecutorWithCache are present in
- // the cache. If they are not present, they are retrieved (via
- // IBurstCallback::getMemories) and added to mExecutorWithCache.
- //
- // This method is locked via mMutex when it is called.
- void ensureCacheEntriesArePresentLocked(const std::vector<int32_t>& slots);
-
- // Work loop that will continue processing execution requests until the
- // ExecutionBurstServer object is freed.
+ // Work loop that will continue processing execution requests until the ExecutionBurstServer
+ // object is freed.
void task();
+ nn::ExecutionResult<std::pair<hidl_vec<OutputShape>, Timing>> execute(
+ const V1_0::Request& requestWithoutPools, const std::vector<int32_t>& slotsOfPools,
+ MeasureTiming measure);
+
std::thread mWorker;
- std::mutex mMutex;
std::atomic<bool> mTeardown{false};
- const sp<hardware::neuralnetworks::V1_2::IBurstCallback> mCallback;
+ const sp<IBurstCallback> mCallback;
const std::unique_ptr<RequestChannelReceiver> mRequestChannelReceiver;
const std::unique_ptr<ResultChannelSender> mResultChannelSender;
- const std::shared_ptr<IBurstExecutorWithCache> mExecutorWithCache;
+ const nn::SharedBurst mBurstExecutor;
+ MemoryCache mMemoryCache;
};
-} // namespace android::nn
+} // namespace android::hardware::neuralnetworks::V1_2::utils
-#endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_SERVER_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
index 8a41591..c662bc3 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
@@ -18,15 +18,16 @@
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_UTILS_H
#include <android/hardware/neuralnetworks/1.0/types.h>
-#include <android/hardware/neuralnetworks/1.1/types.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ProtectCallback.h>
#include <atomic>
#include <chrono>
#include <memory>
-#include <optional>
#include <tuple>
#include <utility>
#include <vector>
@@ -38,159 +39,139 @@
*/
constexpr const size_t kExecutionBurstChannelLength = 1024;
-using FmqRequestDescriptor = MQDescriptorSync<FmqRequestDatum>;
-using FmqResultDescriptor = MQDescriptorSync<FmqResultDatum>;
+/**
+ * Get how long the burst controller should poll while waiting for results to be returned.
+ *
+ * This time can be affected by the property "debug.nn.burst-controller-polling-window".
+ *
+ * @return Polling time in microseconds.
+ */
+std::chrono::microseconds getBurstControllerPollingTimeWindow();
+
+/**
+ * Get how long the burst server should poll while waiting for a request to be received.
+ *
+ * This time can be affected by the property "debug.nn.burst-server-polling-window".
+ *
+ * @return Polling time in microseconds.
+ */
+std::chrono::microseconds getBurstServerPollingTimeWindow();
/**
* Function to serialize a request.
*
- * Prefer calling RequestChannelSender::send.
- *
* @param request Request object without the pool information.
* @param measure Whether to collect timing information for the execution.
- * @param memoryIds Slot identifiers corresponding to memory resources for the
- * request.
+ * @param memoryIds Slot identifiers corresponding to memory resources for the request.
* @return Serialized FMQ request data.
*/
-std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum> serialize(
- const hardware::neuralnetworks::V1_0::Request& request,
- hardware::neuralnetworks::V1_2::MeasureTiming measure, const std::vector<int32_t>& slots);
+std::vector<FmqRequestDatum> serialize(const V1_0::Request& request, MeasureTiming measure,
+ const std::vector<int32_t>& slots);
/**
* Deserialize the FMQ request data.
*
- * The three resulting fields are the Request object (where Request::pools is
- * empty), slot identifiers (which are stand-ins for Request::pools), and
- * whether timing information must be collected for the run.
+ * The three resulting fields are the Request object (where Request::pools is empty), slot
+ * identifiers (which are stand-ins for Request::pools), and whether timing information must be
+ * collected for the run.
*
* @param data Serialized FMQ request data.
- * @return Request object if successfully deserialized, std::nullopt otherwise.
+ * @return Request object if successfully deserialized, otherwise an error message.
*/
-std::optional<std::tuple<hardware::neuralnetworks::V1_0::Request, std::vector<int32_t>,
- hardware::neuralnetworks::V1_2::MeasureTiming>>
-deserialize(const std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>& data);
+nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, MeasureTiming>> deserialize(
+ const std::vector<FmqRequestDatum>& data);
/**
* Function to serialize results.
*
- * Prefer calling ResultChannelSender::send.
- *
* @param errorStatus Status of the execution.
* @param outputShapes Dynamic shapes of the output tensors.
* @param timing Timing information of the execution.
* @return Serialized FMQ result data.
*/
-std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum> serialize(
- hardware::neuralnetworks::V1_0::ErrorStatus errorStatus,
- const std::vector<hardware::neuralnetworks::V1_2::OutputShape>& outputShapes,
- hardware::neuralnetworks::V1_2::Timing timing);
+std::vector<FmqResultDatum> serialize(V1_0::ErrorStatus errorStatus,
+ const std::vector<OutputShape>& outputShapes, Timing timing);
/**
* Deserialize the FMQ result data.
*
- * The three resulting fields are the status of the execution, the dynamic
- * shapes of the output tensors, and the timing information of the execution.
+ * The three resulting fields are the status of the execution, the dynamic shapes of the output
+ * tensors, and the timing information of the execution.
*
* @param data Serialized FMQ result data.
- * @return Result object if successfully deserialized, std::nullopt otherwise.
+ * @return Result object if successfully deserialized, otherwise an error message.
*/
-std::optional<std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
- std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
- hardware::neuralnetworks::V1_2::Timing>>
-deserialize(const std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>& data);
+nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<OutputShape>, Timing>> deserialize(
+ const std::vector<FmqResultDatum>& data);
/**
- * Convert result code to error status.
- *
- * @param resultCode Result code to be converted.
- * @return ErrorStatus Resultant error status.
+ * RequestChannelSender is responsible for serializing the result packet of information, sending it
+ * on the result channel, and signaling that the data is available.
*/
-hardware::neuralnetworks::V1_0::ErrorStatus legacyConvertResultCodeToErrorStatus(int resultCode);
-
-/**
- * RequestChannelSender is responsible for serializing the result packet of
- * information, sending it on the result channel, and signaling that the data is
- * available.
- */
-class RequestChannelSender {
- using FmqRequestDescriptor =
- hardware::MQDescriptorSync<hardware::neuralnetworks::V1_2::FmqRequestDatum>;
- using FmqRequestChannel =
- hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqRequestDatum,
- hardware::kSynchronizedReadWrite>;
+class RequestChannelSender final : public neuralnetworks::utils::IProtectedCallback {
+ struct PrivateConstructorTag {};
public:
/**
* Create the sending end of a request channel.
*
- * Prefer this call over the constructor.
- *
* @param channelLength Number of elements in the FMQ.
- * @return A pair of ResultChannelReceiver and the FMQ descriptor on
- * successful creation, both nullptr otherwise.
+ * @return A pair of ResultChannelReceiver and the FMQ descriptor on successful creation,
+ * GeneralError otherwise.
*/
- static std::pair<std::unique_ptr<RequestChannelSender>, const FmqRequestDescriptor*> create(
- size_t channelLength);
+ static nn::GeneralResult<std::pair<std::unique_ptr<RequestChannelSender>,
+ const MQDescriptorSync<FmqRequestDatum>*>>
+ create(size_t channelLength);
/**
* Send the request to the channel.
*
* @param request Request object without the pool information.
* @param measure Whether to collect timing information for the execution.
- * @param memoryIds Slot identifiers corresponding to memory resources for
- * the request.
- * @return 'true' on successful send, 'false' otherwise.
+ * @param slots Slot identifiers corresponding to memory resources for the request.
+ * @return An empty `Result` on successful send, otherwise an error message.
*/
- bool send(const hardware::neuralnetworks::V1_0::Request& request,
- hardware::neuralnetworks::V1_2::MeasureTiming measure,
- const std::vector<int32_t>& slots);
+ nn::Result<void> send(const V1_0::Request& request, MeasureTiming measure,
+ const std::vector<int32_t>& slots);
/**
- * Method to mark the channel as invalid, causing all future calls to
- * RequestChannelSender::send to immediately return false without attempting
- * to send a message across the FMQ.
+ * Method to mark the channel as invalid, causing all future calls to RequestChannelSender::send
+ * to immediately return false without attempting to send a message across the FMQ.
*/
- void invalidate();
+ void notifyAsDeadObject() override;
// prefer calling RequestChannelSender::send
- bool sendPacket(const std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>& packet);
+ nn::Result<void> sendPacket(const std::vector<FmqRequestDatum>& packet);
- RequestChannelSender(std::unique_ptr<FmqRequestChannel> fmqRequestChannel);
+ RequestChannelSender(PrivateConstructorTag tag, size_t channelLength);
private:
- const std::unique_ptr<FmqRequestChannel> mFmqRequestChannel;
+ MessageQueue<FmqRequestDatum, kSynchronizedReadWrite> mFmqRequestChannel;
std::atomic<bool> mValid{true};
};
/**
- * RequestChannelReceiver is responsible for waiting on the channel until the
- * packet is available, extracting the packet from the channel, and
- * deserializing the packet.
+ * RequestChannelReceiver is responsible for waiting on the channel until the packet is available,
+ * extracting the packet from the channel, and deserializing the packet.
*
- * Because the receiver can wait on a packet that may never come (e.g., because
- * the sending side of the packet has been closed), this object can be
- * invalidated, unblocking the receiver.
+ * Because the receiver can wait on a packet that may never come (e.g., because the sending side of
+ * the packet has been closed), this object can be invalidated, unblocking the receiver.
*/
-class RequestChannelReceiver {
- using FmqRequestChannel =
- hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqRequestDatum,
- hardware::kSynchronizedReadWrite>;
+class RequestChannelReceiver final {
+ struct PrivateConstructorTag {};
public:
/**
* Create the receiving end of a request channel.
*
- * Prefer this call over the constructor.
- *
* @param requestChannel Descriptor for the request channel.
- * @param pollingTimeWindow How much time (in microseconds) the
- * RequestChannelReceiver is allowed to poll the FMQ before waiting on
- * the blocking futex. Polling may result in lower latencies at the
- * potential cost of more power usage.
+ * @param pollingTimeWindow How much time (in microseconds) the RequestChannelReceiver is
+ * allowed to poll the FMQ before waiting on the blocking futex. Polling may result in lower
+ * latencies at the potential cost of more power usage.
* @return RequestChannelReceiver on successful creation, nullptr otherwise.
*/
- static std::unique_ptr<RequestChannelReceiver> create(
- const FmqRequestDescriptor& requestChannel,
+ static nn::GeneralResult<std::unique_ptr<RequestChannelReceiver>> create(
+ const MQDescriptorSync<FmqRequestDatum>& requestChannel,
std::chrono::microseconds pollingTimeWindow);
/**
@@ -200,49 +181,45 @@
* 1) The packet has been retrieved, or
* 2) The receiver has been invalidated
*
- * @return Request object if successfully received, std::nullopt if error or
- * if the receiver object was invalidated.
+ * @return Request object if successfully received, an appropriate message if error or if the
+ * receiver object was invalidated.
*/
- std::optional<std::tuple<hardware::neuralnetworks::V1_0::Request, std::vector<int32_t>,
- hardware::neuralnetworks::V1_2::MeasureTiming>>
- getBlocking();
+ nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, MeasureTiming>> getBlocking();
/**
- * Method to mark the channel as invalid, unblocking any current or future
- * calls to RequestChannelReceiver::getBlocking.
+ * Method to mark the channel as invalid, unblocking any current or future calls to
+ * RequestChannelReceiver::getBlocking.
*/
void invalidate();
- RequestChannelReceiver(std::unique_ptr<FmqRequestChannel> fmqRequestChannel,
+ RequestChannelReceiver(PrivateConstructorTag tag,
+ const MQDescriptorSync<FmqRequestDatum>& requestChannel,
std::chrono::microseconds pollingTimeWindow);
private:
- std::optional<std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>> getPacketBlocking();
+ nn::Result<std::vector<FmqRequestDatum>> getPacketBlocking();
- const std::unique_ptr<FmqRequestChannel> mFmqRequestChannel;
+ MessageQueue<FmqRequestDatum, kSynchronizedReadWrite> mFmqRequestChannel;
std::atomic<bool> mTeardown{false};
const std::chrono::microseconds kPollingTimeWindow;
};
/**
- * ResultChannelSender is responsible for serializing the result packet of
- * information, sending it on the result channel, and signaling that the data is
- * available.
+ * ResultChannelSender is responsible for serializing the result packet of information, sending it
+ * on the result channel, and signaling that the data is available.
*/
-class ResultChannelSender {
- using FmqResultChannel = hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqResultDatum,
- hardware::kSynchronizedReadWrite>;
+class ResultChannelSender final {
+ struct PrivateConstructorTag {};
public:
/**
* Create the sending end of a result channel.
*
- * Prefer this call over the constructor.
- *
* @param resultChannel Descriptor for the result channel.
* @return ResultChannelSender on successful creation, nullptr otherwise.
*/
- static std::unique_ptr<ResultChannelSender> create(const FmqResultDescriptor& resultChannel);
+ static nn::GeneralResult<std::unique_ptr<ResultChannelSender>> create(
+ const MQDescriptorSync<FmqResultDatum>& resultChannel);
/**
* Send the result to the channel.
@@ -250,52 +227,44 @@
* @param errorStatus Status of the execution.
* @param outputShapes Dynamic shapes of the output tensors.
* @param timing Timing information of the execution.
- * @return 'true' on successful send, 'false' otherwise.
*/
- bool send(hardware::neuralnetworks::V1_0::ErrorStatus errorStatus,
- const std::vector<hardware::neuralnetworks::V1_2::OutputShape>& outputShapes,
- hardware::neuralnetworks::V1_2::Timing timing);
+ void send(V1_0::ErrorStatus errorStatus, const std::vector<OutputShape>& outputShapes,
+ Timing timing);
// prefer calling ResultChannelSender::send
- bool sendPacket(const std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>& packet);
+ void sendPacket(const std::vector<FmqResultDatum>& packet);
- ResultChannelSender(std::unique_ptr<FmqResultChannel> fmqResultChannel);
+ ResultChannelSender(PrivateConstructorTag tag,
+ const MQDescriptorSync<FmqResultDatum>& resultChannel);
private:
- const std::unique_ptr<FmqResultChannel> mFmqResultChannel;
+ MessageQueue<FmqResultDatum, kSynchronizedReadWrite> mFmqResultChannel;
};
/**
- * ResultChannelReceiver is responsible for waiting on the channel until the
- * packet is available, extracting the packet from the channel, and
- * deserializing the packet.
+ * ResultChannelReceiver is responsible for waiting on the channel until the packet is available,
+ * extracting the packet from the channel, and deserializing the packet.
*
- * Because the receiver can wait on a packet that may never come (e.g., because
- * the sending side of the packet has been closed), this object can be
- * invalidated, unblocking the receiver.
+ * Because the receiver can wait on a packet that may never come (e.g., because the sending side of
+ * the packet has been closed), this object can be invalidated, unblocking the receiver.
*/
-class ResultChannelReceiver {
- using FmqResultDescriptor =
- hardware::MQDescriptorSync<hardware::neuralnetworks::V1_2::FmqResultDatum>;
- using FmqResultChannel = hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqResultDatum,
- hardware::kSynchronizedReadWrite>;
+class ResultChannelReceiver final : public neuralnetworks::utils::IProtectedCallback {
+ struct PrivateConstructorTag {};
public:
/**
* Create the receiving end of a result channel.
*
- * Prefer this call over the constructor.
- *
* @param channelLength Number of elements in the FMQ.
- * @param pollingTimeWindow How much time (in microseconds) the
- * ResultChannelReceiver is allowed to poll the FMQ before waiting on
- * the blocking futex. Polling may result in lower latencies at the
- * potential cost of more power usage.
- * @return A pair of ResultChannelReceiver and the FMQ descriptor on
- * successful creation, both nullptr otherwise.
+ * @param pollingTimeWindow How much time (in microseconds) the ResultChannelReceiver is allowed
+ * to poll the FMQ before waiting on the blocking futex. Polling may result in lower
+ * latencies at the potential cost of more power usage.
+ * @return A pair of ResultChannelReceiver and the FMQ descriptor on successful creation, or
+ * GeneralError otherwise.
*/
- static std::pair<std::unique_ptr<ResultChannelReceiver>, const FmqResultDescriptor*> create(
- size_t channelLength, std::chrono::microseconds pollingTimeWindow);
+ static nn::GeneralResult<std::pair<std::unique_ptr<ResultChannelReceiver>,
+ const MQDescriptorSync<FmqResultDatum>*>>
+ create(size_t channelLength, std::chrono::microseconds pollingTimeWindow);
/**
* Get the result from the channel.
@@ -304,28 +273,25 @@
* 1) The packet has been retrieved, or
* 2) The receiver has been invalidated
*
- * @return Result object if successfully received, std::nullopt if error or
+ * @return Result object if successfully received, otherwise an appropriate message if error or
* if the receiver object was invalidated.
*/
- std::optional<std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
- std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
- hardware::neuralnetworks::V1_2::Timing>>
- getBlocking();
+ nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<OutputShape>, Timing>> getBlocking();
/**
- * Method to mark the channel as invalid, unblocking any current or future
- * calls to ResultChannelReceiver::getBlocking.
+ * Method to mark the channel as invalid, unblocking any current or future calls to
+ * ResultChannelReceiver::getBlocking.
*/
- void invalidate();
+ void notifyAsDeadObject() override;
// prefer calling ResultChannelReceiver::getBlocking
- std::optional<std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>> getPacketBlocking();
+ nn::Result<std::vector<FmqResultDatum>> getPacketBlocking();
- ResultChannelReceiver(std::unique_ptr<FmqResultChannel> fmqResultChannel,
+ ResultChannelReceiver(PrivateConstructorTag tag, size_t channelLength,
std::chrono::microseconds pollingTimeWindow);
private:
- const std::unique_ptr<FmqResultChannel> mFmqResultChannel;
+ MessageQueue<FmqResultDatum, kSynchronizedReadWrite> mFmqResultChannel;
std::atomic<bool> mValid{true};
const std::chrono::microseconds kPollingTimeWindow;
};
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 86a417a..2c45583 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -331,6 +331,10 @@
return validatedConvert(timing);
}
+GeneralResult<SharedMemory> convert(const hardware::hidl_memory& memory) {
+ return validatedConvert(memory);
+}
+
GeneralResult<std::vector<Extension>> convert(const hidl_vec<hal::V1_2::Extension>& extensions) {
return validatedConvert(extensions);
}
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
index 2265861..eedf591 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -17,283 +17,321 @@
#define LOG_TAG "ExecutionBurstController"
#include "ExecutionBurstController.h"
+#include "ExecutionBurstUtils.h"
#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.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>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
#include <algorithm>
#include <cstring>
#include <limits>
#include <memory>
#include <string>
+#include <thread>
#include <tuple>
#include <utility>
#include <vector>
-#include "ExecutionBurstUtils.h"
-#include "HalInterfaces.h"
+#include "Callbacks.h"
+#include "Conversions.h"
#include "Tracing.h"
#include "Utils.h"
-namespace android::nn {
+namespace android::hardware::neuralnetworks::V1_2::utils {
namespace {
-class BurstContextDeathHandler : public hardware::hidl_death_recipient {
- public:
- using Callback = std::function<void()>;
-
- BurstContextDeathHandler(const Callback& onDeathCallback) : mOnDeathCallback(onDeathCallback) {
- CHECK(onDeathCallback != nullptr);
+nn::GeneralResult<sp<IBurstContext>> executionBurstResultCallback(
+ V1_0::ErrorStatus status, const sp<IBurstContext>& burstContext) {
+ HANDLE_HAL_STATUS(status) << "IPreparedModel::configureExecutionBurst failed with status "
+ << toString(status);
+ if (burstContext == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "IPreparedModel::configureExecutionBurst returned nullptr for burst";
}
-
- void serviceDied(uint64_t /*cookie*/, const wp<hidl::base::V1_0::IBase>& /*who*/) override {
- LOG(ERROR) << "BurstContextDeathHandler::serviceDied -- service unexpectedly died!";
- mOnDeathCallback();
- }
-
- private:
- const Callback mOnDeathCallback;
-};
-
-} // anonymous namespace
-
-hardware::Return<void> ExecutionBurstController::ExecutionBurstCallback::getMemories(
- const hardware::hidl_vec<int32_t>& slots, getMemories_cb cb) {
- std::lock_guard<std::mutex> guard(mMutex);
-
- // get all memories
- hardware::hidl_vec<hardware::hidl_memory> memories(slots.size());
- std::transform(slots.begin(), slots.end(), memories.begin(), [this](int32_t slot) {
- return slot < mMemoryCache.size() ? mMemoryCache[slot] : hardware::hidl_memory{};
- });
-
- // ensure all memories are valid
- if (!std::all_of(memories.begin(), memories.end(),
- [](const hardware::hidl_memory& memory) { return memory.valid(); })) {
- cb(V1_0::ErrorStatus::INVALID_ARGUMENT, {});
- return hardware::Void();
- }
-
- // return successful
- cb(V1_0::ErrorStatus::NONE, std::move(memories));
- return hardware::Void();
+ return burstContext;
}
-std::vector<int32_t> ExecutionBurstController::ExecutionBurstCallback::getSlots(
- const hardware::hidl_vec<hardware::hidl_memory>& memories,
- const std::vector<intptr_t>& keys) {
- std::lock_guard<std::mutex> guard(mMutex);
-
- // retrieve (or bind) all slots corresponding to memories
- std::vector<int32_t> slots;
- slots.reserve(memories.size());
- for (size_t i = 0; i < memories.size(); ++i) {
- slots.push_back(getSlotLocked(memories[i], keys[i]));
+nn::GeneralResult<hidl_vec<hidl_memory>> getMemoriesHelper(
+ const hidl_vec<int32_t>& slots,
+ const std::shared_ptr<ExecutionBurstController::MemoryCache>& memoryCache) {
+ hidl_vec<hidl_memory> memories(slots.size());
+ for (size_t i = 0; i < slots.size(); ++i) {
+ const int32_t slot = slots[i];
+ const auto memory = NN_TRY(memoryCache->getMemory(slot));
+ memories[i] = NN_TRY(V1_0::utils::unvalidatedConvert(memory));
+ if (!memories[i].valid()) {
+ return NN_ERROR() << "memory at slot " << slot << " is invalid";
+ }
}
- return slots;
+ return memories;
}
-std::pair<bool, int32_t> ExecutionBurstController::ExecutionBurstCallback::freeMemory(
- intptr_t key) {
- std::lock_guard<std::mutex> guard(mMutex);
+} // namespace
- auto iter = mMemoryIdToSlot.find(key);
- if (iter == mMemoryIdToSlot.end()) {
- return {false, 0};
- }
- const int32_t slot = iter->second;
- mMemoryIdToSlot.erase(key);
- mMemoryCache[slot] = {};
- mFreeSlots.push(slot);
- return {true, slot};
+// MemoryCache methods
+
+ExecutionBurstController::MemoryCache::MemoryCache() {
+ constexpr size_t kPreallocatedCount = 1024;
+ std::vector<int32_t> freeSlotsSpace;
+ freeSlotsSpace.reserve(kPreallocatedCount);
+ mFreeSlots = std::stack<int32_t, std::vector<int32_t>>(std::move(freeSlotsSpace));
+ mMemoryCache.reserve(kPreallocatedCount);
+ mCacheCleaner.reserve(kPreallocatedCount);
}
-int32_t ExecutionBurstController::ExecutionBurstCallback::getSlotLocked(
- const hardware::hidl_memory& memory, intptr_t key) {
- auto iter = mMemoryIdToSlot.find(key);
- if (iter == mMemoryIdToSlot.end()) {
- const int32_t slot = allocateSlotLocked();
- mMemoryIdToSlot[key] = slot;
- mMemoryCache[slot] = memory;
- return slot;
- } else {
+void ExecutionBurstController::MemoryCache::setBurstContext(sp<IBurstContext> burstContext) {
+ std::lock_guard guard(mMutex);
+ mBurstContext = std::move(burstContext);
+}
+
+std::pair<int32_t, ExecutionBurstController::MemoryCache::SharedCleanup>
+ExecutionBurstController::MemoryCache::cacheMemory(const nn::SharedMemory& memory) {
+ std::unique_lock lock(mMutex);
+ base::ScopedLockAssertion lockAssert(mMutex);
+
+ // Use existing cache entry if (1) the Memory object is in the cache and (2) the cache entry is
+ // not currently being freed.
+ auto iter = mMemoryIdToSlot.find(memory);
+ while (iter != mMemoryIdToSlot.end()) {
const int32_t slot = iter->second;
- return slot;
+ if (auto cleaner = mCacheCleaner.at(slot).lock()) {
+ return std::make_pair(slot, std::move(cleaner));
+ }
+
+ // If the code reaches this point, the Memory object was in the cache, but is currently
+ // being destroyed. This code waits until the cache entry has been freed, then loops to
+ // ensure the cache entry has been freed or has been made present by another thread.
+ mCond.wait(lock);
+ iter = mMemoryIdToSlot.find(memory);
}
+
+ // Allocate a new cache entry.
+ const int32_t slot = allocateSlotLocked();
+ mMemoryIdToSlot[memory] = slot;
+ mMemoryCache[slot] = memory;
+
+ // Create reference-counted self-cleaning cache object.
+ auto self = weak_from_this();
+ Task cleanup = [memory, memoryCache = std::move(self)] {
+ if (const auto lock = memoryCache.lock()) {
+ lock->freeMemory(memory);
+ }
+ };
+ auto cleaner = std::make_shared<const Cleanup>(std::move(cleanup));
+ mCacheCleaner[slot] = cleaner;
+
+ return std::make_pair(slot, std::move(cleaner));
}
-int32_t ExecutionBurstController::ExecutionBurstCallback::allocateSlotLocked() {
+nn::GeneralResult<nn::SharedMemory> ExecutionBurstController::MemoryCache::getMemory(int32_t slot) {
+ std::lock_guard guard(mMutex);
+ if (slot < 0 || static_cast<size_t>(slot) >= mMemoryCache.size()) {
+ return NN_ERROR() << "Invalid slot: " << slot << " vs " << mMemoryCache.size();
+ }
+ return mMemoryCache[slot];
+}
+
+void ExecutionBurstController::MemoryCache::freeMemory(const nn::SharedMemory& memory) {
+ {
+ std::lock_guard guard(mMutex);
+ const int32_t slot = mMemoryIdToSlot.at(memory);
+ if (mBurstContext) {
+ mBurstContext->freeMemory(slot);
+ }
+ mMemoryIdToSlot.erase(memory);
+ mMemoryCache[slot] = {};
+ mCacheCleaner[slot].reset();
+ mFreeSlots.push(slot);
+ }
+ mCond.notify_all();
+}
+
+int32_t ExecutionBurstController::MemoryCache::allocateSlotLocked() {
constexpr size_t kMaxNumberOfSlots = std::numeric_limits<int32_t>::max();
- // if there is a free slot, use it
- if (mFreeSlots.size() > 0) {
+ // If there is a free slot, use it.
+ if (!mFreeSlots.empty()) {
const int32_t slot = mFreeSlots.top();
mFreeSlots.pop();
return slot;
}
- // otherwise use a slot for the first time
- CHECK(mMemoryCache.size() < kMaxNumberOfSlots) << "Exceeded maximum number of slots!";
+ // Use a slot for the first time.
+ CHECK_LT(mMemoryCache.size(), kMaxNumberOfSlots) << "Exceeded maximum number of slots!";
const int32_t slot = static_cast<int32_t>(mMemoryCache.size());
mMemoryCache.emplace_back();
+ mCacheCleaner.emplace_back();
return slot;
}
-std::unique_ptr<ExecutionBurstController> ExecutionBurstController::create(
- const sp<V1_2::IPreparedModel>& preparedModel,
+// ExecutionBurstCallback methods
+
+ExecutionBurstController::ExecutionBurstCallback::ExecutionBurstCallback(
+ const std::shared_ptr<MemoryCache>& memoryCache)
+ : kMemoryCache(memoryCache) {
+ CHECK(memoryCache != nullptr);
+}
+
+Return<void> ExecutionBurstController::ExecutionBurstCallback::getMemories(
+ const hidl_vec<int32_t>& slots, getMemories_cb cb) {
+ const auto memoryCache = kMemoryCache.lock();
+ if (memoryCache == nullptr) {
+ LOG(ERROR) << "ExecutionBurstController::ExecutionBurstCallback::getMemories called after "
+ "the MemoryCache has been freed";
+ cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+ return Void();
+ }
+
+ const auto maybeMemories = getMemoriesHelper(slots, memoryCache);
+ if (!maybeMemories.has_value()) {
+ const auto& [message, code] = maybeMemories.error();
+ LOG(ERROR) << "ExecutionBurstController::ExecutionBurstCallback::getMemories failed with "
+ << code << ": " << message;
+ cb(V1_0::ErrorStatus::INVALID_ARGUMENT, {});
+ return Void();
+ }
+
+ cb(V1_0::ErrorStatus::NONE, maybeMemories.value());
+ return Void();
+}
+
+// ExecutionBurstController methods
+
+nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> ExecutionBurstController::create(
+ const sp<V1_2::IPreparedModel>& preparedModel, FallbackFunction fallback,
std::chrono::microseconds pollingTimeWindow) {
// check inputs
if (preparedModel == nullptr) {
- LOG(ERROR) << "ExecutionBurstController::create passed a nullptr";
- return nullptr;
+ return NN_ERROR() << "ExecutionBurstController::create passed a nullptr";
}
- // create callback object
- sp<ExecutionBurstCallback> callback = new ExecutionBurstCallback();
-
// create FMQ objects
- auto [requestChannelSenderTemp, requestChannelDescriptor] =
- RequestChannelSender::create(kExecutionBurstChannelLength);
- auto [resultChannelReceiverTemp, resultChannelDescriptor] =
- ResultChannelReceiver::create(kExecutionBurstChannelLength, pollingTimeWindow);
- std::shared_ptr<RequestChannelSender> requestChannelSender =
- std::move(requestChannelSenderTemp);
- std::shared_ptr<ResultChannelReceiver> resultChannelReceiver =
- std::move(resultChannelReceiverTemp);
+ auto [requestChannelSender, requestChannelDescriptor] =
+ NN_TRY(RequestChannelSender::create(kExecutionBurstChannelLength));
+ auto [resultChannelReceiver, resultChannelDescriptor] =
+ NN_TRY(ResultChannelReceiver::create(kExecutionBurstChannelLength, pollingTimeWindow));
// check FMQ objects
- if (!requestChannelSender || !resultChannelReceiver || !requestChannelDescriptor ||
- !resultChannelDescriptor) {
- LOG(ERROR) << "ExecutionBurstController::create failed to create FastMessageQueue";
- return nullptr;
- }
+ CHECK(requestChannelSender != nullptr);
+ CHECK(requestChannelDescriptor != nullptr);
+ CHECK(resultChannelReceiver != nullptr);
+ CHECK(resultChannelDescriptor != nullptr);
+
+ // create memory cache
+ auto memoryCache = std::make_shared<MemoryCache>();
+
+ // create callback object
+ auto burstCallback = sp<ExecutionBurstCallback>::make(memoryCache);
+ auto cb = hal::utils::CallbackValue(executionBurstResultCallback);
// configure burst
- V1_0::ErrorStatus errorStatus;
- sp<IBurstContext> burstContext;
- const hardware::Return<void> ret = preparedModel->configureExecutionBurst(
- callback, *requestChannelDescriptor, *resultChannelDescriptor,
- [&errorStatus, &burstContext](V1_0::ErrorStatus status,
- const sp<IBurstContext>& context) {
- errorStatus = status;
- burstContext = context;
- });
+ const Return<void> ret = preparedModel->configureExecutionBurst(
+ burstCallback, *requestChannelDescriptor, *resultChannelDescriptor, cb);
+ HANDLE_TRANSPORT_FAILURE(ret);
- // check burst
- if (!ret.isOk()) {
- LOG(ERROR) << "IPreparedModel::configureExecutionBurst failed with description "
- << ret.description();
- return nullptr;
- }
- if (errorStatus != V1_0::ErrorStatus::NONE) {
- LOG(ERROR) << "IPreparedModel::configureExecutionBurst failed with status "
- << toString(errorStatus);
- return nullptr;
- }
- if (burstContext == nullptr) {
- LOG(ERROR) << "IPreparedModel::configureExecutionBurst returned nullptr for burst";
- return nullptr;
- }
+ auto burstContext = NN_TRY(cb.take());
+ memoryCache->setBurstContext(burstContext);
// create death handler object
- BurstContextDeathHandler::Callback onDeathCallback = [requestChannelSender,
- resultChannelReceiver] {
- requestChannelSender->invalidate();
- resultChannelReceiver->invalidate();
- };
- const sp<BurstContextDeathHandler> deathHandler = new BurstContextDeathHandler(onDeathCallback);
-
- // linkToDeath registers a callback that will be invoked on service death to
- // proactively handle service crashes. If the linkToDeath call fails,
- // asynchronous calls are susceptible to hangs if the service crashes before
- // providing the response.
- const hardware::Return<bool> deathHandlerRet = burstContext->linkToDeath(deathHandler, 0);
- if (!deathHandlerRet.isOk() || deathHandlerRet != true) {
- LOG(ERROR) << "ExecutionBurstController::create -- Failed to register a death recipient "
- "for the IBurstContext object.";
- return nullptr;
- }
+ auto deathHandler = NN_TRY(neuralnetworks::utils::DeathHandler::create(burstContext));
+ deathHandler.protectCallbackForLifetimeOfDeathHandler(requestChannelSender.get());
+ deathHandler.protectCallbackForLifetimeOfDeathHandler(resultChannelReceiver.get());
// make and return controller
- return std::make_unique<ExecutionBurstController>(requestChannelSender, resultChannelReceiver,
- burstContext, callback, deathHandler);
+ return std::make_shared<const ExecutionBurstController>(
+ PrivateConstructorTag{}, std::move(fallback), std::move(requestChannelSender),
+ std::move(resultChannelReceiver), std::move(burstCallback), std::move(burstContext),
+ std::move(memoryCache), std::move(deathHandler));
}
ExecutionBurstController::ExecutionBurstController(
- const std::shared_ptr<RequestChannelSender>& requestChannelSender,
- const std::shared_ptr<ResultChannelReceiver>& resultChannelReceiver,
- const sp<IBurstContext>& burstContext, const sp<ExecutionBurstCallback>& callback,
- const sp<hardware::hidl_death_recipient>& deathHandler)
- : mRequestChannelSender(requestChannelSender),
- mResultChannelReceiver(resultChannelReceiver),
- mBurstContext(burstContext),
- mMemoryCache(callback),
- mDeathHandler(deathHandler) {}
+ PrivateConstructorTag /*tag*/, FallbackFunction fallback,
+ std::unique_ptr<RequestChannelSender> requestChannelSender,
+ std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
+ sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
+ std::shared_ptr<MemoryCache> memoryCache, neuralnetworks::utils::DeathHandler deathHandler)
+ : kFallback(std::move(fallback)),
+ mRequestChannelSender(std::move(requestChannelSender)),
+ mResultChannelReceiver(std::move(resultChannelReceiver)),
+ mBurstCallback(std::move(callback)),
+ mBurstContext(std::move(burstContext)),
+ mMemoryCache(std::move(memoryCache)),
+ kDeathHandler(std::move(deathHandler)) {}
-ExecutionBurstController::~ExecutionBurstController() {
- // It is safe to ignore any errors resulting from this unlinkToDeath call
- // because the ExecutionBurstController object is already being destroyed
- // and its underlying IBurstContext object is no longer being used by the NN
- // runtime.
- if (mDeathHandler) {
- mBurstContext->unlinkToDeath(mDeathHandler).isOk();
+ExecutionBurstController::OptionalCacheHold ExecutionBurstController::cacheMemory(
+ const nn::SharedMemory& memory) const {
+ auto [slot, hold] = mMemoryCache->cacheMemory(memory);
+ return hold;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ExecutionBurstController::execute(const nn::Request& request, nn::MeasureTiming measure) 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
+ // ExecutionBurstServer collects systraces at different points in the code.
+ NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::execute");
+
+ // if the request is valid but of a higher version than what's supported in burst execution,
+ // fall back to another execution path
+ if (const auto version = NN_TRY(hal::utils::makeExecutionFailure(nn::validate(request)));
+ version > nn::Version::ANDROID_Q) {
+ // fallback to another execution path if the packet could not be sent
+ if (kFallback) {
+ return kFallback(request, measure);
+ }
+ return NN_ERROR() << "Request object has features not supported by IBurst::execute";
}
-}
-static std::tuple<int, std::vector<V1_2::OutputShape>, V1_2::Timing, bool> getExecutionResult(
- V1_0::ErrorStatus status, std::vector<V1_2::OutputShape> outputShapes, V1_2::Timing timing,
- bool fallback) {
- auto [n, checkedOutputShapes, checkedTiming] =
- getExecutionResult(convertToV1_3(status), std::move(outputShapes), timing);
- return {n, convertToV1_2(checkedOutputShapes), convertToV1_2(checkedTiming), fallback};
-}
+ // clear pools field of request, as they will be provided via slots
+ const auto requestWithoutPools =
+ nn::Request{.inputs = request.inputs, .outputs = request.outputs, .pools = {}};
+ auto hidlRequest = NN_TRY(
+ hal::utils::makeExecutionFailure(V1_0::utils::unvalidatedConvert(requestWithoutPools)));
+ const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
-std::tuple<int, std::vector<V1_2::OutputShape>, V1_2::Timing, bool>
-ExecutionBurstController::compute(const V1_0::Request& request, V1_2::MeasureTiming measure,
- const std::vector<intptr_t>& memoryIds) {
- // 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 ExecutionBurstServer collects
- // systraces at different points in the code.
- NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::compute");
+ // 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 = base::make_scope_guard([this] { mExecutionInFlight.clear(); });
- std::lock_guard<std::mutex> guard(mMutex);
+ std::vector<int32_t> slots;
+ std::vector<OptionalCacheHold> holds;
+ slots.reserve(request.pools.size());
+ holds.reserve(request.pools.size());
+ for (const auto& memoryPool : request.pools) {
+ auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
+ slots.push_back(slot);
+ holds.push_back(std::move(hold));
+ }
// send request packet
- const std::vector<int32_t> slots = mMemoryCache->getSlots(request.pools, memoryIds);
- const bool success = mRequestChannelSender->send(request, measure, slots);
- if (!success) {
- LOG(ERROR) << "Error sending FMQ packet";
- // only use fallback execution path if the packet could not be sent
- return getExecutionResult(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming12,
- /*fallback=*/true);
+ const auto sendStatus = mRequestChannelSender->send(hidlRequest, hidlMeasure, slots);
+ if (!sendStatus.ok()) {
+ // fallback to another execution path if the packet could not be sent
+ if (kFallback) {
+ return kFallback(request, measure);
+ }
+ return NN_ERROR() << "Error sending FMQ packet: " << sendStatus.error();
}
// get result packet
- const auto result = mResultChannelReceiver->getBlocking();
- if (!result) {
- LOG(ERROR) << "Error retrieving FMQ packet";
- // only use fallback execution path if the packet could not be sent
- return getExecutionResult(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming12,
- /*fallback=*/false);
- }
-
- // unpack results and return (only use fallback execution path if the
- // packet could not be sent)
- auto [status, outputShapes, timing] = std::move(*result);
- return getExecutionResult(status, std::move(outputShapes), timing, /*fallback=*/false);
+ const auto [status, outputShapes, timing] =
+ NN_TRY(hal::utils::makeExecutionFailure(mResultChannelReceiver->getBlocking()));
+ return executionCallback(status, outputShapes, timing);
}
-void ExecutionBurstController::freeMemory(intptr_t key) {
- std::lock_guard<std::mutex> guard(mMutex);
-
- bool valid;
- int32_t slot;
- std::tie(valid, slot) = mMemoryCache->freeMemory(key);
- if (valid) {
- mBurstContext->freeMemory(slot).isOk();
- }
-}
-
-} // namespace android::nn
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
index 022548d..50af881 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
@@ -17,8 +17,19 @@
#define LOG_TAG "ExecutionBurstServer"
#include "ExecutionBurstServer.h"
+#include "Conversions.h"
+#include "ExecutionBurstUtils.h"
#include <android-base/logging.h>
+#include <nnapi/IBurst.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>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
#include <algorithm>
#include <cstring>
@@ -29,134 +40,146 @@
#include <utility>
#include <vector>
-#include "ExecutionBurstUtils.h"
-#include "HalInterfaces.h"
#include "Tracing.h"
-namespace android::nn {
+namespace android::hardware::neuralnetworks::V1_2::utils {
namespace {
-// DefaultBurstExecutorWithCache adapts an IPreparedModel so that it can be
-// used as an IBurstExecutorWithCache. Specifically, the cache simply stores the
-// hidl_memory object, and the execution forwards calls to the provided
-// IPreparedModel's "executeSynchronously" method. With this class, hidl_memory
-// must be mapped and unmapped for each execution.
-class DefaultBurstExecutorWithCache : public ExecutionBurstServer::IBurstExecutorWithCache {
- public:
- DefaultBurstExecutorWithCache(V1_2::IPreparedModel* preparedModel)
- : mpPreparedModel(preparedModel) {}
+using neuralnetworks::utils::makeExecutionFailure;
- bool isCacheEntryPresent(int32_t slot) const override {
- const auto it = mMemoryCache.find(slot);
- return (it != mMemoryCache.end()) && it->second.valid();
+constexpr V1_2::Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
+ std::numeric_limits<uint64_t>::max()};
+
+nn::GeneralResult<std::vector<nn::SharedMemory>> getMemoriesCallback(
+ V1_0::ErrorStatus status, const hidl_vec<hidl_memory>& memories) {
+ HANDLE_HAL_STATUS(status) << "getting burst memories failed with " << toString(status);
+ std::vector<nn::SharedMemory> canonicalMemories;
+ canonicalMemories.reserve(memories.size());
+ for (const auto& memory : memories) {
+ canonicalMemories.push_back(NN_TRY(nn::convert(memory)));
}
-
- void addCacheEntry(const hardware::hidl_memory& memory, int32_t slot) override {
- mMemoryCache[slot] = memory;
- }
-
- void removeCacheEntry(int32_t slot) override { mMemoryCache.erase(slot); }
-
- std::tuple<V1_0::ErrorStatus, hardware::hidl_vec<V1_2::OutputShape>, V1_2::Timing> execute(
- const V1_0::Request& request, const std::vector<int32_t>& slots,
- V1_2::MeasureTiming measure) override {
- // convert slots to pools
- hardware::hidl_vec<hardware::hidl_memory> pools(slots.size());
- std::transform(slots.begin(), slots.end(), pools.begin(),
- [this](int32_t slot) { return mMemoryCache[slot]; });
-
- // create full request
- V1_0::Request fullRequest = request;
- fullRequest.pools = std::move(pools);
-
- // setup execution
- V1_0::ErrorStatus returnedStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
- hardware::hidl_vec<V1_2::OutputShape> returnedOutputShapes;
- V1_2::Timing returnedTiming;
- auto cb = [&returnedStatus, &returnedOutputShapes, &returnedTiming](
- V1_0::ErrorStatus status,
- const hardware::hidl_vec<V1_2::OutputShape>& outputShapes,
- const V1_2::Timing& timing) {
- returnedStatus = status;
- returnedOutputShapes = outputShapes;
- returnedTiming = timing;
- };
-
- // execute
- const hardware::Return<void> ret =
- mpPreparedModel->executeSynchronously(fullRequest, measure, cb);
- if (!ret.isOk() || returnedStatus != V1_0::ErrorStatus::NONE) {
- LOG(ERROR) << "IPreparedModelAdapter::execute -- Error executing";
- return {returnedStatus, std::move(returnedOutputShapes), kNoTiming};
- }
-
- return std::make_tuple(returnedStatus, std::move(returnedOutputShapes), returnedTiming);
- }
-
- private:
- V1_2::IPreparedModel* const mpPreparedModel;
- std::map<int32_t, hardware::hidl_memory> mMemoryCache;
-};
+ return canonicalMemories;
+}
} // anonymous namespace
+ExecutionBurstServer::MemoryCache::MemoryCache(nn::SharedBurst burstExecutor,
+ sp<IBurstCallback> burstCallback)
+ : kBurstExecutor(std::move(burstExecutor)), kBurstCallback(std::move(burstCallback)) {
+ CHECK(kBurstExecutor != nullptr);
+ CHECK(kBurstCallback != nullptr);
+}
+
+nn::GeneralResult<std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>>
+ExecutionBurstServer::MemoryCache::getCacheEntries(const std::vector<int32_t>& slots) {
+ std::lock_guard guard(mMutex);
+ NN_TRY(ensureCacheEntriesArePresentLocked(slots));
+
+ std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>> results;
+ results.reserve(slots.size());
+ for (int32_t slot : slots) {
+ results.push_back(NN_TRY(getCacheEntryLocked(slot)));
+ }
+
+ return results;
+}
+
+nn::GeneralResult<void> ExecutionBurstServer::MemoryCache::ensureCacheEntriesArePresentLocked(
+ const std::vector<int32_t>& slots) {
+ const auto slotIsKnown = [this](int32_t slot)
+ REQUIRES(mMutex) { return mCache.count(slot) > 0; };
+
+ // find unique unknown slots
+ std::vector<int32_t> unknownSlots = slots;
+ std::sort(unknownSlots.begin(), unknownSlots.end());
+ auto unknownSlotsEnd = std::unique(unknownSlots.begin(), unknownSlots.end());
+ unknownSlotsEnd = std::remove_if(unknownSlots.begin(), unknownSlotsEnd, slotIsKnown);
+ unknownSlots.erase(unknownSlotsEnd, unknownSlots.end());
+
+ // quick-exit if all slots are known
+ if (unknownSlots.empty()) {
+ return {};
+ }
+
+ auto cb = neuralnetworks::utils::CallbackValue(getMemoriesCallback);
+
+ const auto ret = kBurstCallback->getMemories(unknownSlots, cb);
+ HANDLE_TRANSPORT_FAILURE(ret);
+
+ auto returnedMemories = NN_TRY(cb.take());
+
+ if (returnedMemories.size() != unknownSlots.size()) {
+ return NN_ERROR()
+ << "ExecutionBurstServer::MemoryCache::ensureCacheEntriesArePresentLocked: Error "
+ "retrieving memories -- count mismatch between requested memories ("
+ << unknownSlots.size() << ") and returned memories (" << returnedMemories.size()
+ << ")";
+ }
+
+ // add memories to unknown slots
+ for (size_t i = 0; i < unknownSlots.size(); ++i) {
+ addCacheEntryLocked(unknownSlots[i], std::move(returnedMemories[i]));
+ }
+
+ return {};
+}
+
+nn::GeneralResult<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>
+ExecutionBurstServer::MemoryCache::getCacheEntryLocked(int32_t slot) {
+ if (const auto iter = mCache.find(slot); iter != mCache.end()) {
+ return iter->second;
+ }
+ return NN_ERROR()
+ << "ExecutionBurstServer::MemoryCache::getCacheEntryLocked failed because slot " << slot
+ << " is not present in the cache";
+}
+
+void ExecutionBurstServer::MemoryCache::addCacheEntryLocked(int32_t slot, nn::SharedMemory memory) {
+ auto hold = kBurstExecutor->cacheMemory(memory);
+ mCache.emplace(slot, std::make_pair(std::move(memory), std::move(hold)));
+}
+
+void ExecutionBurstServer::MemoryCache::removeCacheEntry(int32_t slot) {
+ std::lock_guard guard(mMutex);
+ mCache.erase(slot);
+}
+
// ExecutionBurstServer methods
-sp<ExecutionBurstServer> ExecutionBurstServer::create(
+nn::GeneralResult<sp<ExecutionBurstServer>> ExecutionBurstServer::create(
const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
- const MQDescriptorSync<FmqResultDatum>& resultChannel,
- std::shared_ptr<IBurstExecutorWithCache> executorWithCache,
+ const MQDescriptorSync<FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
std::chrono::microseconds pollingTimeWindow) {
// check inputs
- if (callback == nullptr || executorWithCache == nullptr) {
- LOG(ERROR) << "ExecutionBurstServer::create passed a nullptr";
- return nullptr;
+ if (callback == nullptr || burstExecutor == nullptr) {
+ return NN_ERROR() << "ExecutionBurstServer::create passed a nullptr";
}
// create FMQ objects
- std::unique_ptr<RequestChannelReceiver> requestChannelReceiver =
- RequestChannelReceiver::create(requestChannel, pollingTimeWindow);
- std::unique_ptr<ResultChannelSender> resultChannelSender =
- ResultChannelSender::create(resultChannel);
+ auto requestChannelReceiver =
+ NN_TRY(RequestChannelReceiver::create(requestChannel, pollingTimeWindow));
+ auto resultChannelSender = NN_TRY(ResultChannelSender::create(resultChannel));
// check FMQ objects
- if (!requestChannelReceiver || !resultChannelSender) {
- LOG(ERROR) << "ExecutionBurstServer::create failed to create FastMessageQueue";
- return nullptr;
- }
+ CHECK(requestChannelReceiver != nullptr);
+ CHECK(resultChannelSender != nullptr);
// make and return context
- return new ExecutionBurstServer(callback, std::move(requestChannelReceiver),
- std::move(resultChannelSender), std::move(executorWithCache));
+ return sp<ExecutionBurstServer>::make(PrivateConstructorTag{}, callback,
+ std::move(requestChannelReceiver),
+ std::move(resultChannelSender), std::move(burstExecutor));
}
-sp<ExecutionBurstServer> ExecutionBurstServer::create(
- const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
- const MQDescriptorSync<FmqResultDatum>& resultChannel, V1_2::IPreparedModel* preparedModel,
- std::chrono::microseconds pollingTimeWindow) {
- // check relevant input
- if (preparedModel == nullptr) {
- LOG(ERROR) << "ExecutionBurstServer::create passed a nullptr";
- return nullptr;
- }
-
- // adapt IPreparedModel to have caching
- const std::shared_ptr<DefaultBurstExecutorWithCache> preparedModelAdapter =
- std::make_shared<DefaultBurstExecutorWithCache>(preparedModel);
-
- // make and return context
- return ExecutionBurstServer::create(callback, requestChannel, resultChannel,
- preparedModelAdapter, pollingTimeWindow);
-}
-
-ExecutionBurstServer::ExecutionBurstServer(
- const sp<IBurstCallback>& callback, std::unique_ptr<RequestChannelReceiver> requestChannel,
- std::unique_ptr<ResultChannelSender> resultChannel,
- std::shared_ptr<IBurstExecutorWithCache> executorWithCache)
+ExecutionBurstServer::ExecutionBurstServer(PrivateConstructorTag /*tag*/,
+ const sp<IBurstCallback>& callback,
+ std::unique_ptr<RequestChannelReceiver> requestChannel,
+ std::unique_ptr<ResultChannelSender> resultChannel,
+ nn::SharedBurst burstExecutor)
: mCallback(callback),
mRequestChannelReceiver(std::move(requestChannel)),
mResultChannelSender(std::move(resultChannel)),
- mExecutorWithCache(std::move(executorWithCache)) {
+ mBurstExecutor(std::move(burstExecutor)),
+ mMemoryCache(mBurstExecutor, mCallback) {
// TODO: highly document the threading behavior of this class
mWorker = std::thread([this] { task(); });
}
@@ -170,51 +193,9 @@
mWorker.join();
}
-hardware::Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
- std::lock_guard<std::mutex> hold(mMutex);
- mExecutorWithCache->removeCacheEntry(slot);
- return hardware::Void();
-}
-
-void ExecutionBurstServer::ensureCacheEntriesArePresentLocked(const std::vector<int32_t>& slots) {
- const auto slotIsKnown = [this](int32_t slot) {
- return mExecutorWithCache->isCacheEntryPresent(slot);
- };
-
- // find unique unknown slots
- std::vector<int32_t> unknownSlots = slots;
- auto unknownSlotsEnd = unknownSlots.end();
- std::sort(unknownSlots.begin(), unknownSlotsEnd);
- unknownSlotsEnd = std::unique(unknownSlots.begin(), unknownSlotsEnd);
- unknownSlotsEnd = std::remove_if(unknownSlots.begin(), unknownSlotsEnd, slotIsKnown);
- unknownSlots.erase(unknownSlotsEnd, unknownSlots.end());
-
- // quick-exit if all slots are known
- if (unknownSlots.empty()) {
- return;
- }
-
- V1_0::ErrorStatus errorStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
- std::vector<hardware::hidl_memory> returnedMemories;
- auto cb = [&errorStatus, &returnedMemories](
- V1_0::ErrorStatus status,
- const hardware::hidl_vec<hardware::hidl_memory>& memories) {
- errorStatus = status;
- returnedMemories = memories;
- };
-
- const hardware::Return<void> ret = mCallback->getMemories(unknownSlots, cb);
-
- if (!ret.isOk() || errorStatus != V1_0::ErrorStatus::NONE ||
- returnedMemories.size() != unknownSlots.size()) {
- LOG(ERROR) << "Error retrieving memories";
- return;
- }
-
- // add memories to unknown slots
- for (size_t i = 0; i < unknownSlots.size(); ++i) {
- mExecutorWithCache->addCacheEntry(returnedMemories[i], unknownSlots[i]);
- }
+Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
+ mMemoryCache.removeCacheEntry(slot);
+ return Void();
}
void ExecutionBurstServer::task() {
@@ -223,38 +204,65 @@
// receive request
auto arguments = mRequestChannelReceiver->getBlocking();
- // if the request packet was not properly received, return a generic
- // error and skip the execution
+ // if the request packet was not properly received, return a generic error and skip the
+ // execution
//
- // if the burst is being torn down, skip the execution so the "task"
- // function can end
- if (!arguments) {
+ // if the burst is being torn down, skip the execution so the "task" function can end
+ if (!arguments.has_value()) {
if (!mTeardown) {
mResultChannelSender->send(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
}
continue;
}
- // otherwise begin tracing execution
- NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
- "ExecutionBurstServer getting memory, executing, and returning results");
+ // unpack the arguments; types are Request, std::vector<int32_t>, and MeasureTiming,
+ // respectively
+ const auto [requestWithoutPools, slotsOfPools, measure] = std::move(arguments).value();
- // unpack the arguments; types are Request, std::vector<int32_t>, and
- // MeasureTiming, respectively
- const auto [requestWithoutPools, slotsOfPools, measure] = std::move(*arguments);
-
- // ensure executor with cache has required memory
- std::lock_guard<std::mutex> hold(mMutex);
- ensureCacheEntriesArePresentLocked(slotsOfPools);
-
- // perform computation; types are ErrorStatus, hidl_vec<OutputShape>,
- // and Timing, respectively
- const auto [errorStatus, outputShapes, returnedTiming] =
- mExecutorWithCache->execute(requestWithoutPools, slotsOfPools, measure);
+ auto result = execute(requestWithoutPools, slotsOfPools, measure);
// return result
- mResultChannelSender->send(errorStatus, outputShapes, returnedTiming);
+ if (result.has_value()) {
+ const auto& [outputShapes, timing] = result.value();
+ mResultChannelSender->send(V1_0::ErrorStatus::NONE, outputShapes, timing);
+ } else {
+ const auto& [message, code, outputShapes] = result.error();
+ LOG(ERROR) << "IBurst::execute failed with " << code << ": " << message;
+ mResultChannelSender->send(convert(code).value(), convert(outputShapes).value(),
+ kNoTiming);
+ }
}
}
-} // namespace android::nn
+nn::ExecutionResult<std::pair<hidl_vec<OutputShape>, Timing>> ExecutionBurstServer::execute(
+ const V1_0::Request& requestWithoutPools, const std::vector<int32_t>& slotsOfPools,
+ MeasureTiming measure) {
+ NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
+ "ExecutionBurstServer getting memory, executing, and returning results");
+
+ // ensure executor with cache has required memory
+ const auto cacheEntries =
+ NN_TRY(makeExecutionFailure(mMemoryCache.getCacheEntries(slotsOfPools)));
+
+ // convert request, populating its pools
+ // This code performs an unvalidated convert because the request object without its pools is
+ // invalid because it is incomplete. Instead, the validation is performed after the memory pools
+ // have been added to the request.
+ auto canonicalRequest =
+ NN_TRY(makeExecutionFailure(nn::unvalidatedConvert(requestWithoutPools)));
+ CHECK(canonicalRequest.pools.empty());
+ std::transform(cacheEntries.begin(), cacheEntries.end(),
+ std::back_inserter(canonicalRequest.pools),
+ [](const auto& cacheEntry) { return cacheEntry.first; });
+ NN_TRY(makeExecutionFailure(validate(canonicalRequest)));
+
+ nn::MeasureTiming canonicalMeasure = NN_TRY(makeExecutionFailure(nn::convert(measure)));
+
+ const auto [outputShapes, timing] =
+ NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure));
+
+ return std::make_pair(NN_TRY(makeExecutionFailure(convert(outputShapes))),
+ NN_TRY(makeExecutionFailure(convert(timing))));
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
index f0275f9..1bdde1e 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
@@ -19,11 +19,15 @@
#include "ExecutionBurstUtils.h"
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
#include <android/hardware/neuralnetworks/1.1/types.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ProtectCallback.h>
#include <atomic>
#include <chrono>
@@ -39,84 +43,97 @@
constexpr V1_2::Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max()};
+std::chrono::microseconds getPollingTimeWindow(const std::string& property) {
+ constexpr int32_t kDefaultPollingTimeWindow = 0;
+#ifdef NN_DEBUGGABLE
+ constexpr int32_t kMinPollingTimeWindow = 0;
+ const int32_t selectedPollingTimeWindow =
+ base::GetIntProperty(property, kDefaultPollingTimeWindow, kMinPollingTimeWindow);
+ return std::chrono::microseconds(selectedPollingTimeWindow);
+#else
+ (void)property;
+ return std::chrono::microseconds(kDefaultPollingTimeWindow);
+#endif // NN_DEBUGGABLE
+}
+
+} // namespace
+
+std::chrono::microseconds getBurstControllerPollingTimeWindow() {
+ return getPollingTimeWindow("debug.nn.burst-controller-polling-window");
+}
+
+std::chrono::microseconds getBurstServerPollingTimeWindow() {
+ return getPollingTimeWindow("debug.nn.burst-server-polling-window");
}
// serialize a request into a packet
std::vector<FmqRequestDatum> serialize(const V1_0::Request& request, V1_2::MeasureTiming measure,
const std::vector<int32_t>& slots) {
// count how many elements need to be sent for a request
- size_t count = 2 + request.inputs.size() + request.outputs.size() + request.pools.size();
+ size_t count = 2 + request.inputs.size() + request.outputs.size() + slots.size();
for (const auto& input : request.inputs) {
count += input.dimensions.size();
}
for (const auto& output : request.outputs) {
count += output.dimensions.size();
}
+ CHECK_LE(count, std::numeric_limits<uint32_t>::max());
// create buffer to temporarily store elements
std::vector<FmqRequestDatum> data;
data.reserve(count);
// package packetInfo
- {
- FmqRequestDatum datum;
- datum.packetInformation(
- {/*.packetSize=*/static_cast<uint32_t>(count),
- /*.numberOfInputOperands=*/static_cast<uint32_t>(request.inputs.size()),
- /*.numberOfOutputOperands=*/static_cast<uint32_t>(request.outputs.size()),
- /*.numberOfPools=*/static_cast<uint32_t>(request.pools.size())});
- data.push_back(datum);
- }
+ data.emplace_back();
+ data.back().packetInformation(
+ {.packetSize = static_cast<uint32_t>(count),
+ .numberOfInputOperands = static_cast<uint32_t>(request.inputs.size()),
+ .numberOfOutputOperands = static_cast<uint32_t>(request.outputs.size()),
+ .numberOfPools = static_cast<uint32_t>(slots.size())});
// package input data
for (const auto& input : request.inputs) {
// package operand information
- FmqRequestDatum datum;
- datum.inputOperandInformation(
- {/*.hasNoValue=*/input.hasNoValue,
- /*.location=*/input.location,
- /*.numberOfDimensions=*/static_cast<uint32_t>(input.dimensions.size())});
- data.push_back(datum);
+ data.emplace_back();
+ data.back().inputOperandInformation(
+ {.hasNoValue = input.hasNoValue,
+ .location = input.location,
+ .numberOfDimensions = static_cast<uint32_t>(input.dimensions.size())});
// package operand dimensions
for (uint32_t dimension : input.dimensions) {
- FmqRequestDatum datum;
- datum.inputOperandDimensionValue(dimension);
- data.push_back(datum);
+ data.emplace_back();
+ data.back().inputOperandDimensionValue(dimension);
}
}
// package output data
for (const auto& output : request.outputs) {
// package operand information
- FmqRequestDatum datum;
- datum.outputOperandInformation(
- {/*.hasNoValue=*/output.hasNoValue,
- /*.location=*/output.location,
- /*.numberOfDimensions=*/static_cast<uint32_t>(output.dimensions.size())});
- data.push_back(datum);
+ data.emplace_back();
+ data.back().outputOperandInformation(
+ {.hasNoValue = output.hasNoValue,
+ .location = output.location,
+ .numberOfDimensions = static_cast<uint32_t>(output.dimensions.size())});
// package operand dimensions
for (uint32_t dimension : output.dimensions) {
- FmqRequestDatum datum;
- datum.outputOperandDimensionValue(dimension);
- data.push_back(datum);
+ data.emplace_back();
+ data.back().outputOperandDimensionValue(dimension);
}
}
// package pool identifier
for (int32_t slot : slots) {
- FmqRequestDatum datum;
- datum.poolIdentifier(slot);
- data.push_back(datum);
+ data.emplace_back();
+ data.back().poolIdentifier(slot);
}
// package measureTiming
- {
- FmqRequestDatum datum;
- datum.measureTiming(measure);
- data.push_back(datum);
- }
+ data.emplace_back();
+ data.back().measureTiming(measure);
+
+ CHECK_EQ(data.size(), count);
// return packet
return data;
@@ -137,46 +154,38 @@
data.reserve(count);
// package packetInfo
- {
- FmqResultDatum datum;
- datum.packetInformation({/*.packetSize=*/static_cast<uint32_t>(count),
- /*.errorStatus=*/errorStatus,
- /*.numberOfOperands=*/static_cast<uint32_t>(outputShapes.size())});
- data.push_back(datum);
- }
+ data.emplace_back();
+ data.back().packetInformation({.packetSize = static_cast<uint32_t>(count),
+ .errorStatus = errorStatus,
+ .numberOfOperands = static_cast<uint32_t>(outputShapes.size())});
// package output shape data
for (const auto& operand : outputShapes) {
// package operand information
- FmqResultDatum::OperandInformation info{};
- info.isSufficient = operand.isSufficient;
- info.numberOfDimensions = static_cast<uint32_t>(operand.dimensions.size());
-
- FmqResultDatum datum;
- datum.operandInformation(info);
- data.push_back(datum);
+ data.emplace_back();
+ data.back().operandInformation(
+ {.isSufficient = operand.isSufficient,
+ .numberOfDimensions = static_cast<uint32_t>(operand.dimensions.size())});
// package operand dimensions
for (uint32_t dimension : operand.dimensions) {
- FmqResultDatum datum;
- datum.operandDimensionValue(dimension);
- data.push_back(datum);
+ data.emplace_back();
+ data.back().operandDimensionValue(dimension);
}
}
// package executionTiming
- {
- FmqResultDatum datum;
- datum.executionTiming(timing);
- data.push_back(datum);
- }
+ data.emplace_back();
+ data.back().executionTiming(timing);
+
+ CHECK_EQ(data.size(), count);
// return result
return data;
}
// deserialize request
-std::optional<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>> deserialize(
+nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>> deserialize(
const std::vector<FmqRequestDatum>& data) {
using discriminator = FmqRequestDatum::hidl_discriminator;
@@ -184,8 +193,7 @@
// validate packet information
if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
- LOG(ERROR) << "FMQ Request packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Request packet ill-formed";
}
// unpackage packet information
@@ -198,8 +206,7 @@
// verify packet size
if (data.size() != packetSize) {
- LOG(ERROR) << "FMQ Request packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Request packet ill-formed";
}
// unpackage input operands
@@ -208,8 +215,7 @@
for (size_t operand = 0; operand < numberOfInputOperands; ++operand) {
// validate input operand information
if (data[index].getDiscriminator() != discriminator::inputOperandInformation) {
- LOG(ERROR) << "FMQ Request packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Request packet ill-formed";
}
// unpackage operand information
@@ -226,8 +232,7 @@
for (size_t i = 0; i < numberOfDimensions; ++i) {
// validate dimension
if (data[index].getDiscriminator() != discriminator::inputOperandDimensionValue) {
- LOG(ERROR) << "FMQ Request packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Request packet ill-formed";
}
// unpackage dimension
@@ -240,7 +245,7 @@
// store result
inputs.push_back(
- {/*.hasNoValue=*/hasNoValue, /*.location=*/location, /*.dimensions=*/dimensions});
+ {.hasNoValue = hasNoValue, .location = location, .dimensions = dimensions});
}
// unpackage output operands
@@ -249,8 +254,7 @@
for (size_t operand = 0; operand < numberOfOutputOperands; ++operand) {
// validate output operand information
if (data[index].getDiscriminator() != discriminator::outputOperandInformation) {
- LOG(ERROR) << "FMQ Request packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Request packet ill-formed";
}
// unpackage operand information
@@ -267,8 +271,7 @@
for (size_t i = 0; i < numberOfDimensions; ++i) {
// validate dimension
if (data[index].getDiscriminator() != discriminator::outputOperandDimensionValue) {
- LOG(ERROR) << "FMQ Request packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Request packet ill-formed";
}
// unpackage dimension
@@ -281,7 +284,7 @@
// store result
outputs.push_back(
- {/*.hasNoValue=*/hasNoValue, /*.location=*/location, /*.dimensions=*/dimensions});
+ {.hasNoValue = hasNoValue, .location = location, .dimensions = dimensions});
}
// unpackage pools
@@ -290,8 +293,7 @@
for (size_t pool = 0; pool < numberOfPools; ++pool) {
// validate input operand information
if (data[index].getDiscriminator() != discriminator::poolIdentifier) {
- LOG(ERROR) << "FMQ Request packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Request packet ill-formed";
}
// unpackage operand information
@@ -304,8 +306,7 @@
// validate measureTiming
if (data[index].getDiscriminator() != discriminator::measureTiming) {
- LOG(ERROR) << "FMQ Request packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Request packet ill-formed";
}
// unpackage measureTiming
@@ -314,27 +315,23 @@
// validate packet information
if (index != packetSize) {
- LOG(ERROR) << "FMQ Result packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Result packet ill-formed";
}
// return request
- V1_0::Request request = {/*.inputs=*/inputs, /*.outputs=*/outputs, /*.pools=*/{}};
+ V1_0::Request request = {.inputs = inputs, .outputs = outputs, .pools = {}};
return std::make_tuple(std::move(request), std::move(slots), measure);
}
// deserialize a packet into the result
-std::optional<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
-deserialize(const std::vector<FmqResultDatum>& data) {
+nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>> deserialize(
+ const std::vector<FmqResultDatum>& data) {
using discriminator = FmqResultDatum::hidl_discriminator;
-
- std::vector<V1_2::OutputShape> outputShapes;
size_t index = 0;
// validate packet information
if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
- LOG(ERROR) << "FMQ Result packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Result packet ill-formed";
}
// unpackage packet information
@@ -346,16 +343,16 @@
// verify packet size
if (data.size() != packetSize) {
- LOG(ERROR) << "FMQ Result packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Result packet ill-formed";
}
// unpackage operands
+ std::vector<V1_2::OutputShape> outputShapes;
+ outputShapes.reserve(numberOfOperands);
for (size_t operand = 0; operand < numberOfOperands; ++operand) {
// validate operand information
if (data[index].getDiscriminator() != discriminator::operandInformation) {
- LOG(ERROR) << "FMQ Result packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Result packet ill-formed";
}
// unpackage operand information
@@ -370,8 +367,7 @@
for (size_t i = 0; i < numberOfDimensions; ++i) {
// validate dimension
if (data[index].getDiscriminator() != discriminator::operandDimensionValue) {
- LOG(ERROR) << "FMQ Result packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Result packet ill-formed";
}
// unpackage dimension
@@ -383,13 +379,12 @@
}
// store result
- outputShapes.push_back({/*.dimensions=*/dimensions, /*.isSufficient=*/isSufficient});
+ outputShapes.push_back({.dimensions = dimensions, .isSufficient = isSufficient});
}
// validate execution timing
if (data[index].getDiscriminator() != discriminator::executionTiming) {
- LOG(ERROR) << "FMQ Result packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Result packet ill-formed";
}
// unpackage execution timing
@@ -398,123 +393,113 @@
// validate packet information
if (index != packetSize) {
- LOG(ERROR) << "FMQ Result packet ill-formed";
- return std::nullopt;
+ return NN_ERROR() << "FMQ Result packet ill-formed";
}
// return result
return std::make_tuple(errorStatus, std::move(outputShapes), timing);
}
-V1_0::ErrorStatus legacyConvertResultCodeToErrorStatus(int resultCode) {
- return convertToV1_0(convertResultCodeToErrorStatus(resultCode));
-}
-
// RequestChannelSender methods
-std::pair<std::unique_ptr<RequestChannelSender>, const FmqRequestDescriptor*>
+nn::GeneralResult<
+ std::pair<std::unique_ptr<RequestChannelSender>, const MQDescriptorSync<FmqRequestDatum>*>>
RequestChannelSender::create(size_t channelLength) {
- std::unique_ptr<FmqRequestChannel> fmqRequestChannel =
- std::make_unique<FmqRequestChannel>(channelLength, /*confEventFlag=*/true);
- if (!fmqRequestChannel->isValid()) {
- LOG(ERROR) << "Unable to create RequestChannelSender";
- return {nullptr, nullptr};
+ auto requestChannelSender =
+ std::make_unique<RequestChannelSender>(PrivateConstructorTag{}, channelLength);
+ if (!requestChannelSender->mFmqRequestChannel.isValid()) {
+ return NN_ERROR() << "Unable to create RequestChannelSender";
}
- const FmqRequestDescriptor* descriptor = fmqRequestChannel->getDesc();
- return std::make_pair(std::make_unique<RequestChannelSender>(std::move(fmqRequestChannel)),
- descriptor);
+ const MQDescriptorSync<FmqRequestDatum>* descriptor =
+ requestChannelSender->mFmqRequestChannel.getDesc();
+ return std::make_pair(std::move(requestChannelSender), descriptor);
}
-RequestChannelSender::RequestChannelSender(std::unique_ptr<FmqRequestChannel> fmqRequestChannel)
- : mFmqRequestChannel(std::move(fmqRequestChannel)) {}
+RequestChannelSender::RequestChannelSender(PrivateConstructorTag /*tag*/, size_t channelLength)
+ : mFmqRequestChannel(channelLength, /*configureEventFlagWord=*/true) {}
-bool RequestChannelSender::send(const V1_0::Request& request, V1_2::MeasureTiming measure,
- const std::vector<int32_t>& slots) {
+nn::Result<void> RequestChannelSender::send(const V1_0::Request& request,
+ V1_2::MeasureTiming measure,
+ const std::vector<int32_t>& slots) {
const std::vector<FmqRequestDatum> serialized = serialize(request, measure, slots);
return sendPacket(serialized);
}
-bool RequestChannelSender::sendPacket(const std::vector<FmqRequestDatum>& packet) {
+nn::Result<void> RequestChannelSender::sendPacket(const std::vector<FmqRequestDatum>& packet) {
if (!mValid) {
- return false;
+ return NN_ERROR() << "FMQ object is invalid";
}
- if (packet.size() > mFmqRequestChannel->availableToWrite()) {
- LOG(ERROR)
- << "RequestChannelSender::sendPacket -- packet size exceeds size available in FMQ";
- return false;
+ if (packet.size() > mFmqRequestChannel.availableToWrite()) {
+ return NN_ERROR()
+ << "RequestChannelSender::sendPacket -- packet size exceeds size available in FMQ";
}
- // Always send the packet with "blocking" because this signals the futex and
- // unblocks the consumer if it is waiting on the futex.
- return mFmqRequestChannel->writeBlocking(packet.data(), packet.size());
+ // Always send the packet with "blocking" because this signals the futex and unblocks the
+ // consumer if it is waiting on the futex.
+ const bool success = mFmqRequestChannel.writeBlocking(packet.data(), packet.size());
+ if (!success) {
+ return NN_ERROR()
+ << "RequestChannelSender::sendPacket -- FMQ's writeBlocking returned an error";
+ }
+
+ return {};
}
-void RequestChannelSender::invalidate() {
+void RequestChannelSender::notifyAsDeadObject() {
mValid = false;
}
// RequestChannelReceiver methods
-std::unique_ptr<RequestChannelReceiver> RequestChannelReceiver::create(
- const FmqRequestDescriptor& requestChannel, std::chrono::microseconds pollingTimeWindow) {
- std::unique_ptr<FmqRequestChannel> fmqRequestChannel =
- std::make_unique<FmqRequestChannel>(requestChannel);
+nn::GeneralResult<std::unique_ptr<RequestChannelReceiver>> RequestChannelReceiver::create(
+ const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+ std::chrono::microseconds pollingTimeWindow) {
+ auto requestChannelReceiver = std::make_unique<RequestChannelReceiver>(
+ PrivateConstructorTag{}, requestChannel, pollingTimeWindow);
- if (!fmqRequestChannel->isValid()) {
- LOG(ERROR) << "Unable to create RequestChannelReceiver";
- return nullptr;
+ if (!requestChannelReceiver->mFmqRequestChannel.isValid()) {
+ return NN_ERROR() << "Unable to create RequestChannelReceiver";
}
- if (fmqRequestChannel->getEventFlagWord() == nullptr) {
- LOG(ERROR)
- << "RequestChannelReceiver::create was passed an MQDescriptor without an EventFlag";
- return nullptr;
+ if (requestChannelReceiver->mFmqRequestChannel.getEventFlagWord() == nullptr) {
+ return NN_ERROR()
+ << "RequestChannelReceiver::create was passed an MQDescriptor without an EventFlag";
}
- return std::make_unique<RequestChannelReceiver>(std::move(fmqRequestChannel),
- pollingTimeWindow);
+ return requestChannelReceiver;
}
-RequestChannelReceiver::RequestChannelReceiver(std::unique_ptr<FmqRequestChannel> fmqRequestChannel,
- std::chrono::microseconds pollingTimeWindow)
- : mFmqRequestChannel(std::move(fmqRequestChannel)), kPollingTimeWindow(pollingTimeWindow) {}
+RequestChannelReceiver::RequestChannelReceiver(
+ PrivateConstructorTag /*tag*/, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+ std::chrono::microseconds pollingTimeWindow)
+ : mFmqRequestChannel(requestChannel), kPollingTimeWindow(pollingTimeWindow) {}
-std::optional<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>>
+nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>>
RequestChannelReceiver::getBlocking() {
- const auto packet = getPacketBlocking();
- if (!packet) {
- return std::nullopt;
- }
-
- return deserialize(*packet);
+ const auto packet = NN_TRY(getPacketBlocking());
+ return deserialize(packet);
}
void RequestChannelReceiver::invalidate() {
mTeardown = true;
// force unblock
- // ExecutionBurstServer is by default waiting on a request packet. If the
- // client process destroys its burst object, the server may still be waiting
- // on the futex. This force unblock wakes up any thread waiting on the
- // futex.
- // TODO: look for a different/better way to signal/notify the futex to wake
- // up any thread waiting on it
- FmqRequestDatum datum;
- datum.packetInformation({/*.packetSize=*/0, /*.numberOfInputOperands=*/0,
- /*.numberOfOutputOperands=*/0, /*.numberOfPools=*/0});
- mFmqRequestChannel->writeBlocking(&datum, 1);
+ // ExecutionBurstServer is by default waiting on a request packet. If the client process
+ // destroys its burst object, the server may still be waiting on the futex. This force unblock
+ // wakes up any thread waiting on the futex.
+ const auto data = serialize(V1_0::Request{}, V1_2::MeasureTiming::NO, {});
+ mFmqRequestChannel.writeBlocking(data.data(), data.size());
}
-std::optional<std::vector<FmqRequestDatum>> RequestChannelReceiver::getPacketBlocking() {
+nn::Result<std::vector<FmqRequestDatum>> RequestChannelReceiver::getPacketBlocking() {
if (mTeardown) {
- return std::nullopt;
+ return NN_ERROR() << "FMQ object is being torn down";
}
- // First spend time polling if results are available in FMQ instead of
- // waiting on the futex. Polling is more responsive (yielding lower
- // latencies), but can take up more power, so only poll for a limited period
- // of time.
+ // First spend time polling if results are available in FMQ instead of waiting on the futex.
+ // Polling is more responsive (yielding lower latencies), but can take up more power, so only
+ // poll for a limited period of time.
auto& getCurrentTime = std::chrono::high_resolution_clock::now;
const auto timeToStopPolling = getCurrentTime() + kPollingTimeWindow;
@@ -522,173 +507,146 @@
while (getCurrentTime() < timeToStopPolling) {
// if class is being torn down, immediately return
if (mTeardown.load(std::memory_order_relaxed)) {
- return std::nullopt;
+ return NN_ERROR() << "FMQ object is being torn down";
}
- // Check if data is available. If it is, immediately retrieve it and
- // return.
- const size_t available = mFmqRequestChannel->availableToRead();
+ // Check if data is available. If it is, immediately retrieve it and return.
+ const size_t available = mFmqRequestChannel.availableToRead();
if (available > 0) {
- // This is the first point when we know an execution is occurring,
- // so begin to collect systraces. Note that a similar systrace does
- // not exist at the corresponding point in
- // ResultChannelReceiver::getPacketBlocking because the execution is
- // already in flight.
- NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
- "ExecutionBurstServer getting packet");
std::vector<FmqRequestDatum> packet(available);
- const bool success = mFmqRequestChannel->read(packet.data(), available);
+ const bool success = mFmqRequestChannel.readBlocking(packet.data(), available);
if (!success) {
- LOG(ERROR) << "Error receiving packet";
- return std::nullopt;
+ return NN_ERROR() << "Error receiving packet";
}
- return std::make_optional(std::move(packet));
+ return packet;
}
+
+ std::this_thread::yield();
}
- // If we get to this point, we either stopped polling because it was taking
- // too long or polling was not allowed. Instead, perform a blocking call
- // which uses a futex to save power.
+ // If we get to this point, we either stopped polling because it was taking too long or polling
+ // was not allowed. Instead, perform a blocking call which uses a futex to save power.
// wait for request packet and read first element of request packet
FmqRequestDatum datum;
- bool success = mFmqRequestChannel->readBlocking(&datum, 1);
-
- // This is the first point when we know an execution is occurring, so begin
- // to collect systraces. Note that a similar systrace does not exist at the
- // corresponding point in ResultChannelReceiver::getPacketBlocking because
- // the execution is already in flight.
- NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstServer getting packet");
+ bool success = mFmqRequestChannel.readBlocking(&datum, 1);
// retrieve remaining elements
- // NOTE: all of the data is already available at this point, so there's no
- // need to do a blocking wait to wait for more data. This is known because
- // in FMQ, all writes are published (made available) atomically. Currently,
- // the producer always publishes the entire packet in one function call, so
- // if the first element of the packet is available, the remaining elements
- // are also available.
- const size_t count = mFmqRequestChannel->availableToRead();
+ // NOTE: all of the data is already available at this point, so there's no need to do a blocking
+ // wait to wait for more data. This is known because in FMQ, all writes are published (made
+ // available) atomically. Currently, the producer always publishes the entire packet in one
+ // function call, so if the first element of the packet is available, the remaining elements are
+ // also available.
+ const size_t count = mFmqRequestChannel.availableToRead();
std::vector<FmqRequestDatum> packet(count + 1);
std::memcpy(&packet.front(), &datum, sizeof(datum));
- success &= mFmqRequestChannel->read(packet.data() + 1, count);
+ success &= mFmqRequestChannel.read(packet.data() + 1, count);
// terminate loop
if (mTeardown) {
- return std::nullopt;
+ return NN_ERROR() << "FMQ object is being torn down";
}
// ensure packet was successfully received
if (!success) {
- LOG(ERROR) << "Error receiving packet";
- return std::nullopt;
+ return NN_ERROR() << "Error receiving packet";
}
- return std::make_optional(std::move(packet));
+ return packet;
}
// ResultChannelSender methods
-std::unique_ptr<ResultChannelSender> ResultChannelSender::create(
- const FmqResultDescriptor& resultChannel) {
- std::unique_ptr<FmqResultChannel> fmqResultChannel =
- std::make_unique<FmqResultChannel>(resultChannel);
+nn::GeneralResult<std::unique_ptr<ResultChannelSender>> ResultChannelSender::create(
+ const MQDescriptorSync<FmqResultDatum>& resultChannel) {
+ auto resultChannelSender =
+ std::make_unique<ResultChannelSender>(PrivateConstructorTag{}, resultChannel);
- if (!fmqResultChannel->isValid()) {
- LOG(ERROR) << "Unable to create RequestChannelSender";
- return nullptr;
+ if (!resultChannelSender->mFmqResultChannel.isValid()) {
+ return NN_ERROR() << "Unable to create RequestChannelSender";
}
- if (fmqResultChannel->getEventFlagWord() == nullptr) {
- LOG(ERROR) << "ResultChannelSender::create was passed an MQDescriptor without an EventFlag";
- return nullptr;
+ if (resultChannelSender->mFmqResultChannel.getEventFlagWord() == nullptr) {
+ return NN_ERROR()
+ << "ResultChannelSender::create was passed an MQDescriptor without an EventFlag";
}
- return std::make_unique<ResultChannelSender>(std::move(fmqResultChannel));
+ return resultChannelSender;
}
-ResultChannelSender::ResultChannelSender(std::unique_ptr<FmqResultChannel> fmqResultChannel)
- : mFmqResultChannel(std::move(fmqResultChannel)) {}
+ResultChannelSender::ResultChannelSender(PrivateConstructorTag /*tag*/,
+ const MQDescriptorSync<FmqResultDatum>& resultChannel)
+ : mFmqResultChannel(resultChannel) {}
-bool ResultChannelSender::send(V1_0::ErrorStatus errorStatus,
+void ResultChannelSender::send(V1_0::ErrorStatus errorStatus,
const std::vector<V1_2::OutputShape>& outputShapes,
V1_2::Timing timing) {
const std::vector<FmqResultDatum> serialized = serialize(errorStatus, outputShapes, timing);
- return sendPacket(serialized);
+ sendPacket(serialized);
}
-bool ResultChannelSender::sendPacket(const std::vector<FmqResultDatum>& packet) {
- if (packet.size() > mFmqResultChannel->availableToWrite()) {
+void ResultChannelSender::sendPacket(const std::vector<FmqResultDatum>& packet) {
+ if (packet.size() > mFmqResultChannel.availableToWrite()) {
LOG(ERROR)
<< "ResultChannelSender::sendPacket -- packet size exceeds size available in FMQ";
const std::vector<FmqResultDatum> errorPacket =
serialize(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
- // Always send the packet with "blocking" because this signals the futex
- // and unblocks the consumer if it is waiting on the futex.
- return mFmqResultChannel->writeBlocking(errorPacket.data(), errorPacket.size());
+ // Always send the packet with "blocking" because this signals the futex and unblocks the
+ // consumer if it is waiting on the futex.
+ mFmqResultChannel.writeBlocking(errorPacket.data(), errorPacket.size());
+ } else {
+ // Always send the packet with "blocking" because this signals the futex and unblocks the
+ // consumer if it is waiting on the futex.
+ mFmqResultChannel.writeBlocking(packet.data(), packet.size());
}
-
- // Always send the packet with "blocking" because this signals the futex and
- // unblocks the consumer if it is waiting on the futex.
- return mFmqResultChannel->writeBlocking(packet.data(), packet.size());
}
// ResultChannelReceiver methods
-std::pair<std::unique_ptr<ResultChannelReceiver>, const FmqResultDescriptor*>
+nn::GeneralResult<
+ std::pair<std::unique_ptr<ResultChannelReceiver>, const MQDescriptorSync<FmqResultDatum>*>>
ResultChannelReceiver::create(size_t channelLength, std::chrono::microseconds pollingTimeWindow) {
- std::unique_ptr<FmqResultChannel> fmqResultChannel =
- std::make_unique<FmqResultChannel>(channelLength, /*confEventFlag=*/true);
- if (!fmqResultChannel->isValid()) {
- LOG(ERROR) << "Unable to create ResultChannelReceiver";
- return {nullptr, nullptr};
+ auto resultChannelReceiver = std::make_unique<ResultChannelReceiver>(
+ PrivateConstructorTag{}, channelLength, pollingTimeWindow);
+ if (!resultChannelReceiver->mFmqResultChannel.isValid()) {
+ return NN_ERROR() << "Unable to create ResultChannelReceiver";
}
- const FmqResultDescriptor* descriptor = fmqResultChannel->getDesc();
- return std::make_pair(
- std::make_unique<ResultChannelReceiver>(std::move(fmqResultChannel), pollingTimeWindow),
- descriptor);
+ const MQDescriptorSync<FmqResultDatum>* descriptor =
+ resultChannelReceiver->mFmqResultChannel.getDesc();
+ return std::make_pair(std::move(resultChannelReceiver), descriptor);
}
-ResultChannelReceiver::ResultChannelReceiver(std::unique_ptr<FmqResultChannel> fmqResultChannel,
+ResultChannelReceiver::ResultChannelReceiver(PrivateConstructorTag /*tag*/, size_t channelLength,
std::chrono::microseconds pollingTimeWindow)
- : mFmqResultChannel(std::move(fmqResultChannel)), kPollingTimeWindow(pollingTimeWindow) {}
+ : mFmqResultChannel(channelLength, /*configureEventFlagWord=*/true),
+ kPollingTimeWindow(pollingTimeWindow) {}
-std::optional<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
+nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
ResultChannelReceiver::getBlocking() {
- const auto packet = getPacketBlocking();
- if (!packet) {
- return std::nullopt;
- }
-
- return deserialize(*packet);
+ const auto packet = NN_TRY(getPacketBlocking());
+ return deserialize(packet);
}
-void ResultChannelReceiver::invalidate() {
+void ResultChannelReceiver::notifyAsDeadObject() {
mValid = false;
// force unblock
- // ExecutionBurstController waits on a result packet after sending a
- // request. If the driver containing ExecutionBurstServer crashes, the
- // controller may be waiting on the futex. This force unblock wakes up any
- // thread waiting on the futex.
- // TODO: look for a different/better way to signal/notify the futex to
- // wake up any thread waiting on it
- FmqResultDatum datum;
- datum.packetInformation({/*.packetSize=*/0,
- /*.errorStatus=*/V1_0::ErrorStatus::GENERAL_FAILURE,
- /*.numberOfOperands=*/0});
- mFmqResultChannel->writeBlocking(&datum, 1);
+ // ExecutionBurstController waits on a result packet after sending a request. If the driver
+ // containing ExecutionBurstServer crashes, the controller may be waiting on the futex. This
+ // force unblock wakes up any thread waiting on the futex.
+ const auto data = serialize(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
+ mFmqResultChannel.writeBlocking(data.data(), data.size());
}
-std::optional<std::vector<FmqResultDatum>> ResultChannelReceiver::getPacketBlocking() {
+nn::Result<std::vector<FmqResultDatum>> ResultChannelReceiver::getPacketBlocking() {
if (!mValid) {
- return std::nullopt;
+ return NN_ERROR() << "FMQ object is invalid";
}
- // First spend time polling if results are available in FMQ instead of
- // waiting on the futex. Polling is more responsive (yielding lower
- // latencies), but can take up more power, so only poll for a limited period
- // of time.
+ // First spend time polling if results are available in FMQ instead of waiting on the futex.
+ // Polling is more responsive (yielding lower latencies), but can take up more power, so only
+ // poll for a limited period of time.
auto& getCurrentTime = std::chrono::high_resolution_clock::now;
const auto timeToStopPolling = getCurrentTime() + kPollingTimeWindow;
@@ -696,54 +654,51 @@
while (getCurrentTime() < timeToStopPolling) {
// if class is being torn down, immediately return
if (!mValid.load(std::memory_order_relaxed)) {
- return std::nullopt;
+ return NN_ERROR() << "FMQ object is invalid";
}
- // Check if data is available. If it is, immediately retrieve it and
- // return.
- const size_t available = mFmqResultChannel->availableToRead();
+ // Check if data is available. If it is, immediately retrieve it and return.
+ const size_t available = mFmqResultChannel.availableToRead();
if (available > 0) {
std::vector<FmqResultDatum> packet(available);
- const bool success = mFmqResultChannel->read(packet.data(), available);
+ const bool success = mFmqResultChannel.readBlocking(packet.data(), available);
if (!success) {
- LOG(ERROR) << "Error receiving packet";
- return std::nullopt;
+ return NN_ERROR() << "Error receiving packet";
}
- return std::make_optional(std::move(packet));
+ return packet;
}
+
+ std::this_thread::yield();
}
- // If we get to this point, we either stopped polling because it was taking
- // too long or polling was not allowed. Instead, perform a blocking call
- // which uses a futex to save power.
+ // If we get to this point, we either stopped polling because it was taking too long or polling
+ // was not allowed. Instead, perform a blocking call which uses a futex to save power.
// wait for result packet and read first element of result packet
FmqResultDatum datum;
- bool success = mFmqResultChannel->readBlocking(&datum, 1);
+ bool success = mFmqResultChannel.readBlocking(&datum, 1);
// retrieve remaining elements
- // NOTE: all of the data is already available at this point, so there's no
- // need to do a blocking wait to wait for more data. This is known because
- // in FMQ, all writes are published (made available) atomically. Currently,
- // the producer always publishes the entire packet in one function call, so
- // if the first element of the packet is available, the remaining elements
- // are also available.
- const size_t count = mFmqResultChannel->availableToRead();
+ // NOTE: all of the data is already available at this point, so there's no need to do a blocking
+ // wait to wait for more data. This is known because in FMQ, all writes are published (made
+ // available) atomically. Currently, the producer always publishes the entire packet in one
+ // function call, so if the first element of the packet is available, the remaining elements are
+ // also available.
+ const size_t count = mFmqResultChannel.availableToRead();
std::vector<FmqResultDatum> packet(count + 1);
std::memcpy(&packet.front(), &datum, sizeof(datum));
- success &= mFmqResultChannel->read(packet.data() + 1, count);
+ success &= mFmqResultChannel.read(packet.data() + 1, count);
if (!mValid) {
- return std::nullopt;
+ return NN_ERROR() << "FMQ object is invalid";
}
// ensure packet was successfully received
if (!success) {
- LOG(ERROR) << "Error receiving packet";
- return std::nullopt;
+ return NN_ERROR() << "Error receiving packet";
}
- return std::make_optional(std::move(packet));
+ return packet;
}
} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index 6841c5e..71a4ea8 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -18,6 +18,8 @@
#include "Callbacks.h"
#include "Conversions.h"
+#include "ExecutionBurstController.h"
+#include "ExecutionBurstUtils.h"
#include "Utils.h"
#include <android/hardware/neuralnetworks/1.0/types.h>
@@ -27,12 +29,12 @@
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
-#include <nnapi/hal/1.0/Burst.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
#include <nnapi/hal/ProtectCallback.h>
+#include <chrono>
#include <memory>
#include <tuple>
#include <utility>
@@ -119,7 +121,14 @@
}
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
- return V1_0::utils::Burst::create(shared_from_this());
+ auto self = shared_from_this();
+ auto fallback = [preparedModel = std::move(self)](const nn::Request& request,
+ nn::MeasureTiming measure)
+ -> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
+ return preparedModel->execute(request, measure, {}, {});
+ };
+ const auto pollingTimeWindow = getBurstControllerPollingTimeWindow();
+ return ExecutionBurstController::create(kPreparedModel, std::move(fallback), pollingTimeWindow);
}
std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/1.2/utils/test/DeviceTest.cpp b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
index 9c8adde..215d44c 100644
--- a/neuralnetworks/1.2/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
@@ -772,7 +772,7 @@
EXPECT_NE(result.value(), nullptr);
}
-TEST(DeviceTest, prepareModelFromCacheError) {
+TEST(DeviceTest, prepareModelFromCacheLaunchError) {
// setup call
const auto mockDevice = createMockDevice();
const auto device = Device::create(kName, mockDevice).value();
@@ -790,6 +790,23 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
+TEST(DeviceTest, prepareModelFromCacheReturnError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+ V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
TEST(DeviceTest, prepareModelFromCacheNullptrError) {
// setup call
const auto mockDevice = createMockDevice();
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index 9f69c9e..96d1a1b 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -90,459 +90,25 @@
BASE_MAX = 0xFFFF,
};
-/**
- * Priority given to a prepared model for execution.
- */
-enum Priority : int32_t {
- LOW,
- MEDIUM,
- HIGH,
-};
+%insert Priority
-/**
- * The capabilities of a driver.
- *
- * This represents performance of non-extension operations.
- *
- * Performance of an operation other than {@link OperationType::IF} and
- * {@link OperationType::WHILE} comes from the type of its first operand.
- */
-struct Capabilities {
- /**
- * Driver performance when operating on float32 data but performing
- * calculations with range and/or precision as low as that of the IEEE
- * 754 16-bit floating-point format.
- */
- PerformanceInfo relaxedFloat32toFloat16PerformanceScalar;
- PerformanceInfo relaxedFloat32toFloat16PerformanceTensor;
+%insert Capabilities
- /**
- * Driver performance when operating on a particular data type.
- * In the case of float32 data, this is used when the calculations
- * are not relaxed.
- */
- struct OperandPerformance {
- OperandType type;
- PerformanceInfo info;
- };
+%insert Operation
- /**
- * Performance by operand type. Must be sorted by OperandType.
- *
- * If a particular {@link OperandType} is not present in operandPerformance,
- * its performance is treated as
- * { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
- *
- * Performance does not apply to {@link OperandType::SUBGRAPH}, and a driver
- * must not report operand performance for {@link OperandType::SUBGRAPH}.
- */
- vec<OperandPerformance> operandPerformance;
+%insert OperandLifeTime
- /**
- * Performance of an {@link OperationType::IF} operation is the sum of
- * {@link Capabilities::ifPerformance} and the mean of performance for the
- * two branch subgraphs, where performance for a subgraph is the sum of the
- * performance of all operations within the subgraph.
- */
- PerformanceInfo ifPerformance;
+%insert Operand
- /**
- * Performance of a {@link OperationType::WHILE} operation is the sum of
- * {@link Capabilities::whilePerformance}, performance for the condition
- * subgraph and performance for the body subgraph, where performance for a
- * subgraph is the sum of the performance of all operations within the
- * subgraph.
- */
- PerformanceInfo whilePerformance;
-};
+%insert Model
-/**
- * Describes one operation of the model's graph.
- */
-struct Operation {
- /**
- * The operation type.
- *
- * Besides the values listed in {@link OperationType}, any value above
- * {@link OperationTypeRange::BASE_MAX} is possible and should be interpreted
- * as an extension type according to {@link Model::extensionNameToPrefix}.
- */
- OperationType type;
+%insert Subgraph
- /**
- * Describes the table that contains the indexes of the inputs of the
- * operation. The offset is the index in the operandIndexes table.
- */
- vec<uint32_t> inputs;
+%insert BufferDesc
- /**
- * Describes the table that contains the indexes of the outputs of the
- * operation. The offset is the index in the operandIndexes table.
- */
- vec<uint32_t> outputs;
-};
+%insert BufferRole
-/**
- * How an operand is used.
- */
-enum OperandLifeTime : int32_t {
- /**
- * The operand is internal to the model. It's created by an operation and
- * consumed by other operations. It must be an output operand of
- * exactly one operation.
- */
- TEMPORARY_VARIABLE,
-
- /**
- * The operand is an input of a subgraph. It must not be an output
- * operand of any operation.
- *
- * An operand can't be both input and output of a subgraph.
- */
- SUBGRAPH_INPUT,
-
- /**
- * The operand is an output of a subgraph. It must be an output
- * operand of exactly one operation.
- *
- * An operand can't be both input and output of a subgraph.
- */
- SUBGRAPH_OUTPUT,
-
- /**
- * The operand is a constant found in Model.operandValues. It must
- * not be an output operand of any operation.
- */
- CONSTANT_COPY,
-
- /**
- * The operand is a constant that was specified via a Memory
- * object. It must not be an output operand of any operation.
- */
- CONSTANT_REFERENCE,
-
- /**
- * The operand does not have a value. This is valid only for optional
- * arguments of operations.
- */
- NO_VALUE,
-
- /**
- * The operand is a reference to a subgraph. It must be an input to one
- * or more {@link OperationType::IF} or {@link OperationType::WHILE}
- * operations.
- */
- SUBGRAPH,
-};
-
-/**
- * Describes one operand of the model's graph.
- */
-struct Operand {
- /**
- * The data type.
- *
- * Besides the values listed in {@link OperandType}, any value above
- * {@link OperandTypeRange::BASE_MAX} is possible and should be interpreted
- * as an extension type according to {@link Model::extensionNameToPrefix}.
- */
- OperandType type;
-
- /**
- * Dimensions of the operand.
- *
- * For a scalar operand, dimensions.size() must be 0.
- *
- * A tensor operand with all dimensions specified has "fully
- * specified" dimensions. Whenever possible (i.e., whenever the
- * dimensions are known at model construction time), a tensor
- * operand should have (but is not required to have) fully
- * specified dimensions, in order to enable the best possible
- * performance.
- *
- * If a tensor operand's dimensions are not fully specified, the
- * dimensions of the operand are deduced from the operand
- * dimensions and values of the operation for which that operand
- * is an output or from the corresponding {@link OperationType::IF} or
- * {@link OperationType::WHILE} operation input operand dimensions in the
- * case of referenced subgraph input operands.
- *
- * In the following situations, a tensor operand's dimensions must
- * be fully specified:
- *
- * . The operand has lifetime CONSTANT_COPY or
- * CONSTANT_REFERENCE.
- *
- * . The operand has lifetime SUBGRAPH_INPUT and belongs to the main
- * subgraph. Fully specified dimensions must either be present in the
- * Operand or they must be provided in the corresponding
- * RequestArgument.
- * EXCEPTION: If the input is optional and omitted
- * (by setting the hasNoValue field of the corresponding
- * RequestArgument to true) then it need not have fully
- * specified dimensions.
- *
- * A tensor operand with some number of unspecified dimensions is
- * represented by setting each unspecified dimension to 0.
- *
- * A tensor operand with unspecified rank is represented by providing
- * an empty dimensions vector.
- */
- vec<uint32_t> dimensions;
-
- /**
- * The number of times this operand appears as an operation input.
- *
- * (For example, if this operand appears once in one operation's
- * input list, and three times in another operation's input list,
- * then numberOfConsumers = 4.)
- */
- uint32_t numberOfConsumers;
-
- /**
- * Quantized scale of the operand.
- *
- * Must be 0 when not applicable to an operand type.
- *
- * See {@link OperandType}.
- */
- float scale;
-
- /**
- * Quantized zero-point offset of the operand.
- *
- * Must be 0 when not applicable to an operand type.
- *
- * See {@link OperandType}.
- */
- int32_t zeroPoint;
-
- /**
- * How the operand is used.
- */
- OperandLifeTime lifetime;
-
- /**
- * Where to find the data for this operand.
- * If the lifetime is TEMPORARY_VARIABLE, SUBGRAPH_INPUT, SUBGRAPH_OUTPUT,
- * or NO_VALUE:
- * - All the fields must be 0.
- * If the lifetime is CONSTANT_COPY:
- * - location.poolIndex is 0.
- * - location.offset is the offset in bytes into Model.operandValues.
- * - location.length is set.
- * If the lifetime is CONSTANT_REFERENCE:
- * - location.poolIndex is set.
- * - location.offset is the offset in bytes into the specified pool.
- * - location.length is set.
- * If the lifetime is SUBGRAPH:
- * - location.poolIndex is 0.
- * - location.offset is the index of the referenced subgraph in
- * {@link Model::referenced}.
- * - location.length is 0.
- */
- DataLocation location;
-
- /**
- * Additional parameters specific to a particular operand type.
- */
- @1.2::Operand.ExtraParams extraParams;
-};
-
-/**
- * A Neural Network Model.
- *
- * This includes not only the execution graph, but also constant data such as
- * weights or scalars added at construction time. The only information that
- * may not be known is the shape of the input tensors.
- */
-struct Model {
- /**
- * The top-level subgraph.
- */
- Subgraph main;
-
- /**
- * Referenced subgraphs.
- *
- * Each subgraph is referenced by the main subgraph or at least one other
- * referenced subgraph.
- *
- * There must be no reference cycles.
- */
- vec<Subgraph> referenced;
-
- /**
- * A byte buffer containing operand data that were copied into the model.
- *
- * An operand's value must be located here if and only if Operand::lifetime
- * equals OperandLifeTime::CONSTANT_COPY.
- */
- vec<uint8_t> operandValues;
-
- /**
- * A collection of shared memory pools containing operand values.
- *
- * An operand's value must be located here if and only if Operand::lifetime
- * equals OperandLifeTime::CONSTANT_REFERENCE.
- */
- vec<memory> pools;
-
- /**
- * 'true' indicates TENSOR_FLOAT32 may be calculated with range and/or
- * precision as low as that of the IEEE 754 16-bit floating-point format.
- * 'false' indicates TENSOR_FLOAT32 must be calculated using at least the
- * range and precision of the IEEE 754 32-bit floating-point format.
- */
- bool relaxComputationFloat32toFloat16;
-
- /**
- * The mapping between extension names and prefixes of operand and
- * operation type values.
- *
- * An operand or operation whose numeric type value is above
- * {@link OperandTypeRange::BASE_MAX} or
- * {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
- * as an extension operand. The low
- * {@link @1.2::Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the
- * value correspond to the type ID within the extension and the high
- * {@link @1.2::Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
- * the "prefix", which maps uniquely to the extension name.
- *
- * For example, if a model contains an operation whose value is
- * 0xAAAABBBB and extensionNameToPrefix contains an entry with
- * prefix=0xAAAA and name="vendor.test.test_extension", then
- * the operation should be interpreted as the operation 0xBBBB
- * of the extension named vendor.test.test_extension.
- *
- * This is a one-to-one correspondence. That is, there must be at most one
- * prefix corresponding to each extension name and at most one extension
- * name corresponding to each prefix.
- */
- vec<@1.2::Model.ExtensionNameAndPrefix> extensionNameToPrefix;
-};
-
-/**
- * An excerpt of the execution graph.
- */
-struct Subgraph {
- /**
- * All operands included in the subgraph.
- */
- vec<Operand> operands;
-
- /**
- * All operations included in the subgraph.
- *
- * The operations are sorted into execution order. Every operand
- * with lifetime SUBGRAPH_OUTPUT or TEMPORARY_VARIABLE must be
- * written before it is read.
- */
- vec<Operation> operations;
-
- /**
- * Input indexes of the subgraph. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> inputIndexes;
-
- /**
- * Output indexes of the subgraph. There must be at least one.
- *
- * Each value corresponds to the index of the operand in "operands".
- */
- vec<uint32_t> outputIndexes;
-};
-
-/**
- * A buffer descriptor. Describes the properties of a buffer.
- */
-struct BufferDesc {
- /**
- * Dimensions of the buffer. May have unknown dimensions or rank. A buffer with some number
- * of unspecified dimensions is represented by setting each unspecified dimension to 0. A
- * buffer with unspecified rank is represented by providing an empty dimensions vector.
- */
- vec<uint32_t> dimensions;
-};
-
-/**
- * Describes a role of an input or output to a prepared model.
- */
-struct BufferRole {
- /**
- * The index of the IPreparedModel within the "preparedModel" argument passed in
- * IDevice::allocate.
- */
- uint32_t modelIndex;
-
- /**
- * The index of the input or output operand.
- */
- uint32_t ioIndex;
-
- /**
- * A floating-point value within the range (0.0, 1.0]. Describes how likely the
- * buffer is to be 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;
-};
-
-/**
- * Inputs to be sent to and outputs to be retrieved from a prepared model.
- *
- * A Request serves two primary tasks:
- * 1) Provides the input and output data to be used when executing the model.
- * 2) Specifies any updates to the input operand metadata that were left
- * unspecified at model preparation time.
- *
- * An output must not overlap with any other output, with an input, or
- * with an operand of lifetime CONSTANT_REFERENCE.
- */
-struct Request {
- /**
- * Input data and information to be used in the execution of a prepared
- * model.
- *
- * The index of the input corresponds to the index in Model.main.inputIndexes.
- * E.g., input[i] corresponds to Model.main.inputIndexes[i].
- */
- vec<RequestArgument> inputs;
-
- /**
- * Output data and information to be used in the execution of a prepared
- * model.
- *
- * The index of the output corresponds to the index in Model.main.outputIndexes.
- * E.g., output[i] corresponds to Model.main.outputIndexes[i].
- */
- vec<RequestArgument> outputs;
-
- /**
- * A memory pool.
- */
- safe_union MemoryPool {
- /**
- * Specifies a client-managed shared memory pool.
- */
- memory hidlMemory;
-
- /**
- * Specifies a driver-managed buffer. It is the token returned from IDevice::allocate,
- * and is specific to the IDevice object.
- */
- uint32_t token;
- };
-
- /**
- * A collection of memory pools containing operand data for both the
- * inputs and the outputs to a model.
- */
- vec<MemoryPool> pools;
-};
+%insert Request
/**
* Optional time point of the steady clock (as from std::chrono::steady_clock)
diff --git a/neuralnetworks/1.3/utils/Android.bp b/neuralnetworks/1.3/utils/Android.bp
index 2b1dcc4..28c036a 100644
--- a/neuralnetworks/1.3/utils/Android.bp
+++ b/neuralnetworks/1.3/utils/Android.bp
@@ -42,6 +42,7 @@
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
"android.hardware.neuralnetworks@1.3",
+ "libfmq",
],
export_static_lib_headers: [
"neuralnetworks_utils_hal_common",
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
index 8e1cdb8..b677c62 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
@@ -59,7 +59,6 @@
GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus);
GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle);
-GeneralResult<SharedMemory> convert(const hardware::hidl_memory& memory);
GeneralResult<std::vector<BufferRole>> convert(
const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles);
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 320c74c..8b45f71 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -244,7 +244,7 @@
return BufferRole{
.modelIndex = bufferRole.modelIndex,
.ioIndex = bufferRole.ioIndex,
- .frequency = bufferRole.frequency,
+ .probability = bufferRole.frequency,
};
}
@@ -352,10 +352,6 @@
return validatedConvert(handle);
}
-GeneralResult<SharedMemory> convert(const hardware::hidl_memory& memory) {
- return validatedConvert(memory);
-}
-
GeneralResult<std::vector<BufferRole>> convert(
const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles) {
return validatedConvert(bufferRoles);
@@ -581,7 +577,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 725e4f5..64275a3 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -29,8 +29,9 @@
#include <nnapi/Result.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
-#include <nnapi/hal/1.0/Burst.h>
#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/ExecutionBurstController.h>
+#include <nnapi/hal/1.2/ExecutionBurstUtils.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
#include <nnapi/hal/ProtectCallback.h>
@@ -199,7 +200,15 @@
}
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
- return V1_0::utils::Burst::create(shared_from_this());
+ auto self = shared_from_this();
+ auto fallback = [preparedModel = std::move(self)](const nn::Request& request,
+ nn::MeasureTiming measure)
+ -> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
+ return preparedModel->execute(request, measure, {}, {});
+ };
+ const auto pollingTimeWindow = V1_2::utils::getBurstControllerPollingTimeWindow();
+ return V1_2::utils::ExecutionBurstController::create(kPreparedModel, std::move(fallback),
+ pollingTimeWindow);
}
std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/1.3/utils/test/DeviceTest.cpp b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
index f260990..2d1b2f2 100644
--- a/neuralnetworks/1.3/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
@@ -794,7 +794,7 @@
EXPECT_NE(result.value(), nullptr);
}
-TEST(DeviceTest, prepareModelFromCacheError) {
+TEST(DeviceTest, prepareModelFromCacheLaunchError) {
// setup call
const auto mockDevice = createMockDevice();
const auto device = Device::create(kName, mockDevice).value();
@@ -812,6 +812,23 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
+TEST(DeviceTest, prepareModelFromCacheReturnError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+ V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
TEST(DeviceTest, prepareModelFromCacheNullptrError) {
// setup call
const auto mockDevice = createMockDevice();
diff --git a/neuralnetworks/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
index 5d168d2..d296828 100644
--- a/neuralnetworks/TEST_MAPPING
+++ b/neuralnetworks/TEST_MAPPING
@@ -16,6 +16,9 @@
"name": "neuralnetworks_utils_hal_1_3_test"
},
{
+ "name": "neuralnetworks_utils_hal_aidl_test"
+ },
+ {
"name": "VtsHalNeuralnetworksV1_0TargetTest",
"options": [
{
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
index 71b7758..05cec76 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
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 c2d636c..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
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -35,5 +36,5 @@
parcelable BufferRole {
int modelIndex;
int ioIndex;
- float frequency;
+ float probability;
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl
index 01cc753..30877c0 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl
index 074cc09..db49a38 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -36,4 +37,5 @@
int poolIndex;
long offset;
long length;
+ long padding;
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl
index 7bc8aa7..7cdd6db 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl
index 1abacc8..82fe8ae 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl
index 873c584..57d5d6e 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl
index c4badc0..4352d8f 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl
index b99bb31..44e9922 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl
index a7ae942..c47028d 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
index 4c25538..6c287fd 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
index b32b217..a3680aa 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl
index 2fee136..7e61bbb 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl
index 2860692..f10e7e2 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl
similarity index 79%
copy from biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
copy to neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl
index c5288fd..634f39e 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -31,10 +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.common;
+package android.hardware.neuralnetworks;
@VintfStability
-parcelable HardwareInfo {
- String deviceName;
- String hardwareVersion;
- String serialNumber;
+interface IBurst {
+ android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in long[] memoryIdentifierTokens, in boolean measureTiming, in long deadline, in long loopTimeoutDuration);
+ void releaseMemoryResource(in long memoryIdentifierToken);
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl
index 4c5fd2f..b328b29 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
index abe67b8..0bfb80a 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl
index 1f7cbe0..52882cd 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -35,6 +36,7 @@
interface IPreparedModel {
android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long deadline, in long loopTimeoutDuration);
android.hardware.neuralnetworks.FencedExecutionResult executeFenced(in android.hardware.neuralnetworks.Request request, in ParcelFileDescriptor[] waitFor, in boolean measureTiming, in long deadline, in long loopTimeoutDuration, in long duration);
+ android.hardware.neuralnetworks.IBurst configureExecutionBurst();
const long DEFAULT_LOOP_TIMEOUT_DURATION_NS = 2000000000;
const long MAXIMUM_LOOP_TIMEOUT_DURATION_NS = 15000000000;
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
index 8eaaab6..e0c763b 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
index 8388fda..dbedf12 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
index 3b2f240..8207b25 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl
index 9d12e58..30d8dda 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
index c1e87da..9314760 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
index bb78caa..5a9f4ff 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl
index 3f6d93b..14792cf 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl
index d581ced..40adfb1 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
index 87fd3a6..de93d8b 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl
index 186c13d..9f2c759 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
index fec83a8..33fcd60 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
index ad42b02..de3b438 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl
index 09a43f7..f733505 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl
index 178946c..04910f5 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl
index d9b77fa..8f35709 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl
index 599b3f4..39ec7a9 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl
index 91b9aa7..e3541c0 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl
index 3813b51..312f581 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl
index dec976f..b7d4451 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
index 66fdfe7..02d68f9 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl
index d0de34a..9690e01 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
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/android/hardware/neuralnetworks/DataLocation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl
index f6b5e0d..f656360 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl
@@ -18,6 +18,28 @@
/**
* Describes the location of a data object.
+ *
+ * If the data object is an omitted operand, all of the fields must be 0. If the poolIndex refers to
+ * a driver-managed buffer allocated from IDevice::allocate, or an AHardwareBuffer of a format other
+ * than AHARDWAREBUFFER_FORMAT_BLOB, the offset, length, and padding must be set to 0 indicating
+ * the entire pool is used.
+ *
+ * Otherwise, the offset, length, and padding specify a sub-region of a memory pool. The sum of
+ * offset, length, and padding must not exceed the total size of the specified memory pool. If the
+ * data object is a scalar operand or a tensor operand with fully specified dimensions, the value of
+ * length must be equal to the raw size of the operand (i.e. the size of an element multiplied
+ * by the number of elements). When used in Operand, the value of padding must be 0. When used in
+ * RequestArgument, the value of padding specifies the extra bytes at the end of the memory region
+ * that may be used by the device to access memory in chunks, for efficiency. If the data object is
+ * a Request output whose dimensions are not fully specified, the value of length specifies the
+ * total size of the writable region of the output data, and padding specifies the extra bytes at
+ * the end of the memory region that may be used by the device to access memory in chunks, for
+ * efficiency, but must not be used to hold any output data.
+ *
+ * When used in RequestArgument, clients should prefer to align and pad the sub-region to
+ * 64 bytes when possible; this may allow the device to access the sub-region more efficiently.
+ * The sub-region is aligned to 64 bytes if the value of offset is a multiple of 64.
+ * The sub-region is padded to 64 bytes if the sum of length and padding is a multiple of 64.
*/
@VintfStability
parcelable DataLocation {
@@ -33,4 +55,8 @@
* The length of the data in bytes.
*/
long length;
+ /**
+ * The end padding of the specified memory region in bytes.
+ */
+ long padding;
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl
new file mode 100644
index 0000000..85d2a03
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl
@@ -0,0 +1,120 @@
+/*
+ * 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.neuralnetworks;
+
+import android.hardware.neuralnetworks.ErrorStatus;
+import android.hardware.neuralnetworks.ExecutionResult;
+import android.hardware.neuralnetworks.Request;
+
+/**
+ * IBurst represents a burst execution object.
+ *
+ * Burst executions are a sequence of executions of the same prepared model that occur in rapid
+ * succession, such as frames of a camera capture or successive audio samples. A burst object is
+ * used to control a set of burst executions, and to preserve resources between executions, enabling
+ * executions to have lower overhead. Burst objects enable some optimizations:
+ * (1) A burst object is created before a sequence of executions, and freed when the sequence has
+ * ended. Because of this, the lifetime of the burst object hints to a driver how long it should
+ * remain in a high performance state.
+ * (2) A burst object can preserve resources between executions. For example, a driver can map a
+ * memory object on the first execution and cache the mapping in the burst object for reuse in
+ * subsequent executions. Any cached resource can be released when the burst object is destroyed
+ * or when the NNAPI runtime notifies the burst object that the resource is no longer required.
+ * (3) A burst object may be used for at most one execution at a time. This enables any transient
+ * execution resources such as intermediate tensors to be allocated once when the burst object
+ * is created and freed when the burst object is destroyed.
+ */
+@VintfStability
+interface IBurst {
+ /**
+ * Performs a synchronous execution on a burst object.
+ *
+ * The execution is performed synchronously with respect to the caller. executeSynchronously
+ * must verify the inputs to the function are correct, and the usages of memory pools allocated
+ * by IDevice::allocate are valid. If there is an error, executeSynchronously must immediately
+ * return a service specific exception with the appropriate ErrorStatus value. If the inputs to
+ * the function are valid and there is no error, executeSynchronously must perform the
+ * execution, and must not return until the execution is complete.
+ *
+ * The caller must not change the content of any data object referenced by 'request' (described
+ * by the {@link DataLocation} of a {@link RequestArgument}) until executeSynchronously returns.
+ * executeSynchronously must not change the content of any of the data objects corresponding to
+ * 'request' inputs.
+ *
+ * If the burst object was configured from a prepared model wherein all tensor operands have
+ * fully specified dimensions, and the inputs to the function are valid, and at execution time
+ * every operation's input operands have legal values, then the execution should complete
+ * successfully: there must be no failure unless the device itself is in a bad state.
+ *
+ * executeSynchronously may be called with an optional deadline. If the execution is not able to
+ * be completed before the provided deadline, the execution may be aborted, and either
+ * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due to an abort must be
+ * sent the same way as other errors, described above.
+ *
+ * Only a single execution on a given burst object may be active at any time.
+ *
+ * @param request The input and output information on which the prepared model is to be
+ * executed.
+ * @param memoryIdentifierTokens A list of tokens where each token is a non-negative number
+ * that uniquely identifies a memory object. Each memory
+ * identifier token corresponds to an element of request.pools. A
+ * value of -1 indicates no identity.
+ * @param measure Specifies whether or not to measure duration of the execution. The duration
+ * runs from the time the driver sees the call to the executeSynchronously
+ * function to the time the driver returns from the function.
+ * @param deadline The time by which the execution is expected to complete. The time is measured
+ * in nanoseconds since epoch of the steady clock (as from
+ * std::chrono::steady_clock). If the execution cannot be finished by the
+ * deadline, the execution may be aborted. Passing -1 means the deadline is
+ * omitted. Other negative values are invalid.
+ * @param loopTimeoutDuration The maximum amount of time in nanoseconds that should be spent
+ * executing a {@link OperationType::WHILE} operation. If a loop
+ * condition model does not output false within this duration, the
+ * execution must be aborted. If -1 is provided, the maximum amount
+ * of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
+ * negative values are invalid. When provided, the duration must not
+ * exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
+ * @return ExecutionResult parcelable, containing the status of the execution, output shapes and
+ * timing information.
+ * @throws ServiceSpecificException with one of the following ErrorStatus values:
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * - INVALID_ARGUMENT if one of the input arguments is invalid
+ * - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the
+ * deadline
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+ */
+ ExecutionResult executeSynchronously(in Request request, in long[] memoryIdentifierTokens,
+ in boolean measureTiming, in long deadline, in long loopTimeoutDuration);
+
+ /**
+ * releaseMemoryResource is used by the client to signal to the service that a memory buffer
+ * corresponding to a slot number is no longer needed by the client, and any cached resources
+ * associated with that memory object may be released.
+ *
+ * The identifier tokens are unique to the burst object.
+ *
+ * @param memoryIdentifierToken Value uniquely identifying a memory object that is no longer
+ * used.
+ * @throws ServiceSpecificException with one of the following ErrorStatus values:
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * - INVALID_ARGUMENT if one of the input arguments is invalid
+ */
+ void releaseMemoryResource(in long memoryIdentifierToken);
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
index 0240e3c..2a9757b 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
@@ -20,6 +20,7 @@
import android.hardware.neuralnetworks.ErrorStatus;
import android.hardware.neuralnetworks.ExecutionResult;
import android.hardware.neuralnetworks.FencedExecutionResult;
+import android.hardware.neuralnetworks.IBurst;
import android.hardware.neuralnetworks.Request;
/**
@@ -166,4 +167,22 @@
FencedExecutionResult executeFenced(in Request request, in ParcelFileDescriptor[] waitFor,
in boolean measureTiming, in long deadline, in long loopTimeoutDuration,
in long duration);
+
+ /**
+ * Configure a Burst object used to execute multiple inferences on a prepared model in rapid
+ * succession.
+ *
+ * If the prepared model was prepared from a model wherein all tensor operands have fully
+ * specified dimensions, and a valid serialized Request is sent to the Burst for execution, and
+ * at execution time every operation's input operands have legal values, then the execution
+ * should complete successfully (ErrorStatus::NONE): There must be no failure unless the device
+ * itself is in a bad state.
+ *
+ * @return burst Execution burst controller object.
+ * @throws ServiceSpecificException with one of the following ErrorStatus values:
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+ */
+ IBurst configureExecutionBurst();
}
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 2673cae..476dac9 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -29,10 +29,12 @@
srcs: ["src/*"],
local_include_dirs: ["include/nnapi/hal/aidl/"],
export_include_dirs: ["include"],
+ cflags: ["-Wthread-safety"],
static_libs: [
"libarect",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
+ "neuralnetworks_utils_hal_1_0",
],
shared_libs: [
"android.hardware.neuralnetworks-V1-ndk_platform",
@@ -41,3 +43,38 @@
"libnativewindow",
],
}
+
+cc_test {
+ name: "neuralnetworks_utils_hal_aidl_test",
+ defaults: ["neuralnetworks_utils_defaults"],
+ srcs: [
+ "test/*.cpp",
+ ],
+ static_libs: [
+ "android.hardware.common-V2-ndk_platform",
+ "android.hardware.neuralnetworks-V1-ndk_platform",
+ "libgmock",
+ "libneuralnetworks_common",
+ "neuralnetworks_types",
+ "neuralnetworks_utils_hal_aidl",
+ "neuralnetworks_utils_hal_common",
+ ],
+ shared_libs: [
+ "android.hidl.allocator@1.0",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libnativewindow",
+ "libutils",
+ ],
+ cflags: [
+ /* GMOCK defines functions for printing all MOCK_DEVICE arguments and
+ * MockDevice contains a string pointer which triggers a warning in the
+ * base logging library. */
+ "-Wno-user-defined-warnings",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Buffer.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Buffer.h
new file mode 100644
index 0000000..46190c4
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Buffer.h
@@ -0,0 +1,56 @@
+/*
+ * 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_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BUFFER_H
+
+#include <aidl/android/hardware/neuralnetworks/IBuffer.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// Class that adapts aidl_hal::IBuffer to nn::IBuffer.
+class Buffer final : public nn::IBuffer {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const Buffer>> create(
+ std::shared_ptr<aidl_hal::IBuffer> buffer, nn::Request::MemoryDomainToken token);
+
+ Buffer(PrivateConstructorTag tag, std::shared_ptr<aidl_hal::IBuffer> buffer,
+ nn::Request::MemoryDomainToken token);
+
+ nn::Request::MemoryDomainToken getToken() const override;
+
+ nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
+ nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
+ const nn::Dimensions& dimensions) const override;
+
+ private:
+ const std::shared_ptr<aidl_hal::IBuffer> kBuffer;
+ const nn::Request::MemoryDomainToken kToken;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BUFFER_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
new file mode 100644
index 0000000..8651912
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
@@ -0,0 +1,53 @@
+/*
+ * 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_CALLBACKS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_CALLBACKS_H
+
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModelCallback.h>
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/TransferValue.h>
+#include <nnapi/hal/aidl/ProtectCallback.h>
+
+// 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 {
+
+// An AIDL callback class to receive the results of IDevice::prepareModel* asynchronously.
+class PreparedModelCallback final : public BnPreparedModelCallback,
+ public hal::utils::IProtectedCallback {
+ public:
+ using Data = nn::GeneralResult<nn::SharedPreparedModel>;
+
+ ndk::ScopedAStatus notify(ErrorStatus status,
+ const std::shared_ptr<IPreparedModel>& preparedModel) override;
+
+ void notifyAsDeadObject() override;
+
+ Data get();
+
+ private:
+ hal::utils::TransferValue<Data> mData;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_CALLBACKS_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
index 1b2f69c..4922a6e 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
@@ -46,6 +46,7 @@
#include <aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.h>
#include <aidl/android/hardware/neuralnetworks/Timing.h>
+#include <android/binder_auto_utils.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
@@ -96,7 +97,11 @@
const aidl_hal::ExtensionOperandTypeInformation& operandTypeInformation);
GeneralResult<SharedHandle> unvalidatedConvert(
const ::aidl::android::hardware::common::NativeHandle& handle);
+GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence);
+GeneralResult<Capabilities> convert(const aidl_hal::Capabilities& capabilities);
+GeneralResult<DeviceType> convert(const aidl_hal::DeviceType& deviceType);
+GeneralResult<ErrorStatus> convert(const aidl_hal::ErrorStatus& errorStatus);
GeneralResult<ExecutionPreference> convert(
const aidl_hal::ExecutionPreference& executionPreference);
GeneralResult<SharedMemory> convert(const aidl_hal::Memory& memory);
@@ -106,9 +111,14 @@
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);
GeneralResult<std::vector<uint32_t>> toUnsigned(const std::vector<int32_t>& vec);
@@ -118,14 +128,62 @@
namespace nn = ::android::nn;
+nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(const nn::CacheToken& cacheToken);
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole);
+nn::GeneralResult<bool> unvalidatedConvert(const nn::MeasureTiming& measureTiming);
nn::GeneralResult<Memory> unvalidatedConvert(const nn::SharedMemory& memory);
nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape);
nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus);
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+ const nn::ExecutionPreference& executionPreference);
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& operandLifeTime);
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location);
+nn::GeneralResult<std::optional<OperandExtraParams>> unvalidatedConvert(
+ const nn::Operand::ExtraParams& extraParams);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph);
+nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(
+ const nn::Model::OperandValues& operandValues);
+nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert(
+ const nn::Model::ExtensionNameAndPrefix& extensionNameToPrefix);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority);
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request);
+nn::GeneralResult<RequestArgument> unvalidatedConvert(const nn::Request::Argument& requestArgument);
+nn::GeneralResult<RequestMemoryPool> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool);
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing);
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::Duration& duration);
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalDuration& optionalDuration);
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalTimePoint& optionalTimePoint);
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::SyncFence& syncFence);
+nn::GeneralResult<common::NativeHandle> unvalidatedConvert(const nn::SharedHandle& sharedHandle);
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvertCache(
+ const nn::SharedHandle& handle);
+nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken);
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<bool> convert(const nn::MeasureTiming& measureTiming);
nn::GeneralResult<Memory> convert(const nn::SharedMemory& memory);
nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<Priority> convert(const nn::Priority& priority);
+nn::GeneralResult<Request> convert(const nn::Request& request);
+nn::GeneralResult<Timing> convert(const nn::Timing& timing);
+nn::GeneralResult<int64_t> convert(const nn::OptionalDuration& optionalDuration);
+nn::GeneralResult<int64_t> convert(const nn::OptionalTimePoint& optionalTimePoint);
+
+nn::GeneralResult<std::vector<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles);
nn::GeneralResult<std::vector<OutputShape>> convert(
const std::vector<nn::OutputShape>& outputShapes);
+nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
+ const std::vector<nn::SharedHandle>& handles);
+nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
+ const std::vector<nn::SyncFence>& syncFences);
nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& vec);
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
new file mode 100644
index 0000000..eb194e3
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
@@ -0,0 +1,98 @@
+/*
+ * 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_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_DEVICE_H
+
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/aidl/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::IDevice to nn::IDevice.
+class Device final : public nn::IDevice {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const Device>> create(
+ std::string name, std::shared_ptr<aidl_hal::IDevice> device);
+
+ Device(PrivateConstructorTag tag, std::string name, std::string versionString,
+ nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+ nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+ std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler);
+
+ const std::string& getName() const override;
+ const std::string& getVersionString() const override;
+ nn::Version getFeatureLevel() const override;
+ nn::DeviceType getType() const override;
+ bool isUpdatable() const override;
+ const std::vector<nn::Extension>& getSupportedExtensions() const override;
+ const nn::Capabilities& getCapabilities() const override;
+ std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+ nn::GeneralResult<void> wait() const override;
+
+ nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+ const nn::Model& model) const override;
+
+ nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+ const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+ nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+ const std::vector<nn::SharedHandle>& dataCache,
+ const nn::CacheToken& token) const override;
+
+ nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+ nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+ const std::vector<nn::SharedHandle>& dataCache,
+ const nn::CacheToken& token) const override;
+
+ nn::GeneralResult<nn::SharedBuffer> allocate(
+ const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+ const std::vector<nn::BufferRole>& inputRoles,
+ const std::vector<nn::BufferRole>& outputRoles) const override;
+
+ DeathMonitor* getDeathMonitor() const;
+
+ private:
+ const std::string kName;
+ const std::string kVersionString;
+ const nn::DeviceType kDeviceType;
+ const std::vector<nn::Extension> kExtensions;
+ const nn::Capabilities kCapabilities;
+ const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+ const std::shared_ptr<aidl_hal::IDevice> kDevice;
+ const DeathHandler kDeathHandler;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_DEVICE_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
new file mode 100644
index 0000000..9b28588
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
@@ -0,0 +1,70 @@
+/*
+ * 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_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PREPARED_MODEL_H
+
+#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/aidl/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// 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::IPreparedModel to nn::IPreparedModel.
+class PreparedModel final : public nn::IPreparedModel,
+ public std::enable_shared_from_this<PreparedModel> {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
+ std::shared_ptr<aidl_hal::IPreparedModel> preparedModel);
+
+ PreparedModel(PrivateConstructorTag tag,
+ std::shared_ptr<aidl_hal::IPreparedModel> preparedModel);
+
+ 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;
+
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced(
+ const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+ nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+ nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
+ std::any getUnderlyingResource() const override;
+
+ private:
+ const std::shared_ptr<aidl_hal::IPreparedModel> kPreparedModel;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PREPARED_MODEL_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
new file mode 100644
index 0000000..ab1108c
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
@@ -0,0 +1,81 @@
+/*
+ * 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_PROTECT_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PROTECT_CALLBACK_H
+
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_interface_utils.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <mutex>
+#include <vector>
+
+// 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 {
+
+// Thread safe class
+class DeathMonitor final {
+ public:
+ static void serviceDied(void* cookie);
+ void serviceDied();
+ // Precondition: `killable` must be non-null.
+ void add(hal::utils::IProtectedCallback* killable) const;
+ // Precondition: `killable` must be non-null.
+ void remove(hal::utils::IProtectedCallback* killable) const;
+
+ private:
+ mutable std::mutex mMutex;
+ mutable std::vector<hal::utils::IProtectedCallback*> mObjects GUARDED_BY(mMutex);
+};
+
+class DeathHandler final {
+ public:
+ static nn::GeneralResult<DeathHandler> create(std::shared_ptr<ndk::ICInterface> object);
+
+ DeathHandler(const DeathHandler&) = delete;
+ DeathHandler(DeathHandler&&) noexcept = default;
+ DeathHandler& operator=(const DeathHandler&) = delete;
+ DeathHandler& operator=(DeathHandler&&) noexcept = delete;
+ ~DeathHandler();
+
+ using Cleanup = std::function<void()>;
+ // Precondition: `killable` must be non-null.
+ [[nodiscard]] ::android::base::ScopeGuard<Cleanup> protectCallback(
+ hal::utils::IProtectedCallback* killable) const;
+
+ std::shared_ptr<DeathMonitor> getDeathMonitor() const { return kDeathMonitor; }
+
+ private:
+ DeathHandler(std::shared_ptr<ndk::ICInterface> object,
+ ndk::ScopedAIBinder_DeathRecipient deathRecipient,
+ std::shared_ptr<DeathMonitor> deathMonitor);
+
+ std::shared_ptr<ndk::ICInterface> kObject;
+ ndk::ScopedAIBinder_DeathRecipient kDeathRecipient;
+ std::shared_ptr<DeathMonitor> kDeathMonitor;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PROTECT_CALLBACK_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Service.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Service.h
new file mode 100644
index 0000000..cb6ff4b
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Service.h
@@ -0,0 +1,32 @@
+/*
+ * 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_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <string>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+::android::nn::GeneralResult<::android::nn::SharedDevice> getDevice(const std::string& name);
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_SERVICE_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
index 79b511d..58dcfe3 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
@@ -23,6 +23,7 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
+#include <nnapi/hal/HandleError.h>
namespace aidl::android::hardware::neuralnetworks::utils {
@@ -52,6 +53,12 @@
nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool);
nn::GeneralResult<Model> clone(const Model& model);
+nn::GeneralResult<void> handleTransportError(const ndk::ScopedAStatus& ret);
+
+#define HANDLE_ASTATUS(ret) \
+ for (const auto status = handleTransportError(ret); !status.ok();) \
+ return NN_ERROR(status.error().code) << status.error().message << ": "
+
} // namespace aidl::android::hardware::neuralnetworks::utils
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_H
diff --git a/neuralnetworks/aidl/utils/src/Buffer.cpp b/neuralnetworks/aidl/utils/src/Buffer.cpp
new file mode 100644
index 0000000..c729a68
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Buffer.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 "Buffer.h"
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include "Conversions.h"
+#include "Utils.h"
+#include "nnapi/hal/aidl/Conversions.h"
+
+#include <memory>
+#include <utility>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const Buffer>> Buffer::create(
+ std::shared_ptr<aidl_hal::IBuffer> buffer, nn::Request::MemoryDomainToken token) {
+ if (buffer == nullptr) {
+ return NN_ERROR() << "aidl_hal::utils::Buffer::create must have non-null buffer";
+ }
+ if (token == static_cast<nn::Request::MemoryDomainToken>(0)) {
+ return NN_ERROR() << "aidl_hal::utils::Buffer::create must have non-zero token";
+ }
+
+ return std::make_shared<const Buffer>(PrivateConstructorTag{}, std::move(buffer), token);
+}
+
+Buffer::Buffer(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBuffer> buffer,
+ nn::Request::MemoryDomainToken token)
+ : kBuffer(std::move(buffer)), kToken(token) {
+ CHECK(kBuffer != nullptr);
+ CHECK(kToken != static_cast<nn::Request::MemoryDomainToken>(0));
+}
+
+nn::Request::MemoryDomainToken Buffer::getToken() const {
+ return kToken;
+}
+
+nn::GeneralResult<void> Buffer::copyTo(const nn::SharedMemory& dst) const {
+ const auto aidlDst = NN_TRY(convert(dst));
+
+ const auto ret = kBuffer->copyTo(aidlDst);
+ HANDLE_ASTATUS(ret) << "IBuffer::copyTo failed";
+
+ return {};
+}
+
+nn::GeneralResult<void> Buffer::copyFrom(const nn::SharedMemory& src,
+ const nn::Dimensions& dimensions) const {
+ const auto aidlSrc = NN_TRY(convert(src));
+ const auto aidlDimensions = NN_TRY(toSigned(dimensions));
+
+ const auto ret = kBuffer->copyFrom(aidlSrc, aidlDimensions);
+ HANDLE_ASTATUS(ret) << "IBuffer::copyFrom failed";
+
+ return {};
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Callbacks.cpp b/neuralnetworks/aidl/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..8055665
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Callbacks.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "ProtectCallback.h"
+#include "Utils.h"
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#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 {
+namespace {
+
+// Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
+// function returns with a non-null nn::SharedPreparedModel with a feature level of
+// nn::Version::ANDROID_S. On failure, this function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+ ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
+ HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ return NN_TRY(PreparedModel::create(preparedModel));
+}
+
+} // namespace
+
+ndk::ScopedAStatus PreparedModelCallback::notify(
+ ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
+ mData.put(prepareModelCallback(status, preparedModel));
+ return ndk::ScopedAStatus::ok();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+ mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+ return mData.take();
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index db3504b..45bc005 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -18,6 +18,8 @@
#include <aidl/android/hardware/common/NativeHandle.h>
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <android/binder_auto_utils.h>
#include <android/hardware_buffer.h>
#include <cutils/native_handle.h>
#include <nnapi/OperandTypes.h>
@@ -42,14 +44,17 @@
#define VERIFY_NON_NEGATIVE(value) \
while (UNLIKELY(value < 0)) return NN_ERROR()
-namespace {
+#define VERIFY_LE_INT32_MAX(value) \
+ while (UNLIKELY(value > std::numeric_limits<int32_t>::max())) return NN_ERROR()
+namespace {
template <typename Type>
constexpr std::underlying_type_t<Type> underlyingType(Type value) {
return static_cast<std::underlying_type_t<Type>>(value);
}
constexpr auto kVersion = android::nn::Version::ANDROID_S;
+constexpr int64_t kNoTiming = -1;
} // namespace
@@ -134,13 +139,8 @@
std::vector<base::unique_fd> fds;
fds.reserve(aidlNativeHandle.fds.size());
for (const auto& fd : aidlNativeHandle.fds) {
- const int dupFd = dup(fd.get());
- if (dupFd == -1) {
- // TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return
- // here?
- return NN_ERROR() << "Failed to dup the fd";
- }
- fds.emplace_back(dupFd);
+ auto duplicatedFd = NN_TRY(dupFd(fd.get()));
+ fds.emplace_back(duplicatedFd.release());
}
return Handle{.fds = std::move(fds), .ints = aidlNativeHandle.ints};
@@ -157,16 +157,12 @@
using UniqueNativeHandle = std::unique_ptr<native_handle_t, NativeHandleDeleter>;
-static nn::GeneralResult<UniqueNativeHandle> nativeHandleFromAidlHandle(
- const NativeHandle& handle) {
+static GeneralResult<UniqueNativeHandle> nativeHandleFromAidlHandle(const NativeHandle& handle) {
std::vector<base::unique_fd> fds;
fds.reserve(handle.fds.size());
for (const auto& fd : handle.fds) {
- const int dupFd = dup(fd.get());
- if (dupFd == -1) {
- return NN_ERROR() << "Failed to dup the fd";
- }
- fds.emplace_back(dupFd);
+ auto duplicatedFd = NN_TRY(dupFd(fd.get()));
+ fds.emplace_back(duplicatedFd.release());
}
constexpr size_t kIntMax = std::numeric_limits<int>::max();
@@ -254,16 +250,22 @@
VERIFY_NON_NEGATIVE(location.poolIndex) << "DataLocation: pool index must not be negative";
VERIFY_NON_NEGATIVE(location.offset) << "DataLocation: offset must not be negative";
VERIFY_NON_NEGATIVE(location.length) << "DataLocation: length must not be negative";
+ VERIFY_NON_NEGATIVE(location.padding) << "DataLocation: padding must not be negative";
if (location.offset > std::numeric_limits<uint32_t>::max()) {
return NN_ERROR() << "DataLocation: offset must be <= std::numeric_limits<uint32_t>::max()";
}
if (location.length > std::numeric_limits<uint32_t>::max()) {
return NN_ERROR() << "DataLocation: length must be <= std::numeric_limits<uint32_t>::max()";
}
+ if (location.padding > std::numeric_limits<uint32_t>::max()) {
+ return NN_ERROR()
+ << "DataLocation: padding must be <= std::numeric_limits<uint32_t>::max()";
+ }
return DataLocation{
.poolIndex = static_cast<uint32_t>(location.poolIndex),
.offset = static_cast<uint32_t>(location.offset),
.length = static_cast<uint32_t>(location.length),
+ .padding = static_cast<uint32_t>(location.padding),
};
}
@@ -382,14 +384,14 @@
GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory) {
VERIFY_NON_NEGATIVE(memory.size) << "Memory size must not be negative";
- if (memory.size > std::numeric_limits<uint32_t>::max()) {
+ if (memory.size > std::numeric_limits<size_t>::max()) {
return NN_ERROR() << "Memory: size must be <= std::numeric_limits<size_t>::max()";
}
if (memory.name != "hardware_buffer_blob") {
return std::make_shared<const Memory>(Memory{
.handle = NN_TRY(unvalidatedConvertHelper(memory.handle)),
- .size = static_cast<uint32_t>(memory.size),
+ .size = static_cast<size_t>(memory.size),
.name = memory.name,
});
}
@@ -434,11 +436,28 @@
return std::make_shared<const Memory>(Memory{
.handle = HardwareBufferHandle(hardwareBuffer, /*takeOwnership=*/true),
- .size = static_cast<uint32_t>(memory.size),
+ .size = static_cast<size_t>(memory.size),
.name = memory.name,
});
}
+GeneralResult<Timing> unvalidatedConvert(const aidl_hal::Timing& timing) {
+ if (timing.timeInDriver < -1) {
+ return NN_ERROR() << "Timing: timeInDriver must not be less than -1";
+ }
+ if (timing.timeOnDevice < -1) {
+ return NN_ERROR() << "Timing: timeOnDevice must not be less than -1";
+ }
+ constexpr auto convertTiming = [](int64_t halTiming) -> OptionalDuration {
+ if (halTiming == kNoTiming) {
+ return {};
+ }
+ return nn::Duration(static_cast<uint64_t>(halTiming));
+ };
+ return Timing{.timeOnDevice = convertTiming(timing.timeOnDevice),
+ .timeInDriver = convertTiming(timing.timeInDriver)};
+}
+
GeneralResult<Model::OperandValues> unvalidatedConvert(const std::vector<uint8_t>& operandValues) {
return Model::OperandValues(operandValues.data(), operandValues.size());
}
@@ -453,7 +472,7 @@
return BufferRole{
.modelIndex = static_cast<uint32_t>(bufferRole.modelIndex),
.ioIndex = static_cast<uint32_t>(bufferRole.ioIndex),
- .frequency = bufferRole.frequency,
+ .probability = bufferRole.probability,
};
}
@@ -515,6 +534,23 @@
return std::make_shared<const Handle>(NN_TRY(unvalidatedConvertHelper(aidlNativeHandle)));
}
+GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence) {
+ auto duplicatedFd = NN_TRY(dupFd(syncFence.get()));
+ return SyncFence::create(std::move(duplicatedFd));
+}
+
+GeneralResult<Capabilities> convert(const aidl_hal::Capabilities& capabilities) {
+ return validatedConvert(capabilities);
+}
+
+GeneralResult<DeviceType> convert(const aidl_hal::DeviceType& deviceType) {
+ return validatedConvert(deviceType);
+}
+
+GeneralResult<ErrorStatus> convert(const aidl_hal::ErrorStatus& errorStatus) {
+ return validatedConvert(errorStatus);
+}
+
GeneralResult<ExecutionPreference> convert(
const aidl_hal::ExecutionPreference& executionPreference) {
return validatedConvert(executionPreference);
@@ -548,6 +584,18 @@
return validatedConvert(request);
}
+GeneralResult<Timing> convert(const aidl_hal::Timing& timing) {
+ return validatedConvert(timing);
+}
+
+GeneralResult<SyncFence> convert(const ndk::ScopedFileDescriptor& syncFence) {
+ return unvalidatedConvert(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);
}
@@ -556,6 +604,11 @@
return validatedConvert(memories);
}
+GeneralResult<std::vector<OutputShape>> convert(
+ const std::vector<aidl_hal::OutputShape>& outputShapes) {
+ return validatedConvert(outputShapes);
+}
+
GeneralResult<std::vector<uint32_t>> toUnsigned(const std::vector<int32_t>& vec) {
if (!std::all_of(vec.begin(), vec.end(), [](int32_t v) { return v >= 0; })) {
return NN_ERROR() << "Negative value passed to conversion from signed to unsigned";
@@ -575,14 +628,21 @@
template <typename Type>
nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
const std::vector<Type>& arguments) {
- std::vector<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
- for (size_t i = 0; i < arguments.size(); ++i) {
- halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
+ std::vector<UnvalidatedConvertOutput<Type>> halObject;
+ halObject.reserve(arguments.size());
+ for (const auto& argument : arguments) {
+ halObject.push_back(NN_TRY(unvalidatedConvert(argument)));
}
return halObject;
}
template <typename Type>
+nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+ const std::vector<Type>& arguments) {
+ return unvalidatedConvertVec(arguments);
+}
+
+template <typename Type>
nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
const auto maybeVersion = nn::validate(canonical);
if (!maybeVersion.has_value()) {
@@ -609,29 +669,29 @@
common::NativeHandle aidlNativeHandle;
aidlNativeHandle.fds.reserve(handle.fds.size());
for (const auto& fd : handle.fds) {
- const int dupFd = dup(fd.get());
- if (dupFd == -1) {
- // TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return
- // here?
- return NN_ERROR() << "Failed to dup the fd";
- }
- aidlNativeHandle.fds.emplace_back(dupFd);
+ auto duplicatedFd = NN_TRY(nn::dupFd(fd.get()));
+ aidlNativeHandle.fds.emplace_back(duplicatedFd.release());
}
aidlNativeHandle.ints = handle.ints;
return aidlNativeHandle;
}
+// Helper template for std::visit
+template <class... Ts>
+struct overloaded : Ts... {
+ using Ts::operator()...;
+};
+template <class... Ts>
+overloaded(Ts...)->overloaded<Ts...>;
+
static nn::GeneralResult<common::NativeHandle> aidlHandleFromNativeHandle(
const native_handle_t& handle) {
common::NativeHandle aidlNativeHandle;
aidlNativeHandle.fds.reserve(handle.numFds);
for (int i = 0; i < handle.numFds; ++i) {
- const int dupFd = dup(handle.data[i]);
- if (dupFd == -1) {
- return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
- }
- aidlNativeHandle.fds.emplace_back(dupFd);
+ auto duplicatedFd = NN_TRY(nn::dupFd(handle.data[i]));
+ aidlNativeHandle.fds.emplace_back(duplicatedFd.release());
}
aidlNativeHandle.ints = std::vector<int>(&handle.data[handle.numFds],
@@ -642,6 +702,30 @@
} // namespace
+nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(const nn::CacheToken& cacheToken) {
+ return std::vector<uint8_t>(cacheToken.begin(), cacheToken.end());
+}
+
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc) {
+ return BufferDesc{.dimensions = NN_TRY(toSigned(bufferDesc.dimensions))};
+}
+
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole) {
+ VERIFY_LE_INT32_MAX(bufferRole.modelIndex)
+ << "BufferRole: modelIndex must be <= std::numeric_limits<int32_t>::max()";
+ VERIFY_LE_INT32_MAX(bufferRole.ioIndex)
+ << "BufferRole: ioIndex must be <= std::numeric_limits<int32_t>::max()";
+ return BufferRole{
+ .modelIndex = static_cast<int32_t>(bufferRole.modelIndex),
+ .ioIndex = static_cast<int32_t>(bufferRole.ioIndex),
+ .probability = bufferRole.probability,
+ };
+}
+
+nn::GeneralResult<bool> unvalidatedConvert(const nn::MeasureTiming& measureTiming) {
+ return measureTiming == nn::MeasureTiming::YES;
+}
+
nn::GeneralResult<common::NativeHandle> unvalidatedConvert(const nn::SharedHandle& sharedHandle) {
CHECK(sharedHandle != nullptr);
return unvalidatedConvert(*sharedHandle);
@@ -707,6 +791,230 @@
.isSufficient = outputShape.isSufficient};
}
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+ const nn::ExecutionPreference& executionPreference) {
+ return static_cast<ExecutionPreference>(executionPreference);
+}
+
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
+ return static_cast<OperandType>(operandType);
+}
+
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(
+ const nn::Operand::LifeTime& operandLifeTime) {
+ return static_cast<OperandLifeTime>(operandLifeTime);
+}
+
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location) {
+ VERIFY_LE_INT32_MAX(location.poolIndex)
+ << "DataLocation: pool index must be <= std::numeric_limits<int32_t>::max()";
+ return DataLocation{
+ .poolIndex = static_cast<int32_t>(location.poolIndex),
+ .offset = static_cast<int64_t>(location.offset),
+ .length = static_cast<int64_t>(location.length),
+ };
+}
+
+nn::GeneralResult<std::optional<OperandExtraParams>> unvalidatedConvert(
+ const nn::Operand::ExtraParams& extraParams) {
+ return std::visit(
+ overloaded{
+ [](const nn::Operand::NoParams&)
+ -> nn::GeneralResult<std::optional<OperandExtraParams>> {
+ return std::nullopt;
+ },
+ [](const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams)
+ -> nn::GeneralResult<std::optional<OperandExtraParams>> {
+ if (symmPerChannelQuantParams.channelDim >
+ std::numeric_limits<int32_t>::max()) {
+ // Using explicit type conversion because std::optional in successful
+ // result confuses the compiler.
+ return (NN_ERROR() << "symmPerChannelQuantParams.channelDim must be <= "
+ "std::numeric_limits<int32_t>::max(), received: "
+ << symmPerChannelQuantParams.channelDim)
+ .
+ operator nn::GeneralResult<std::optional<OperandExtraParams>>();
+ }
+ return OperandExtraParams::make<OperandExtraParams::Tag::channelQuant>(
+ SymmPerChannelQuantParams{
+ .scales = symmPerChannelQuantParams.scales,
+ .channelDim = static_cast<int32_t>(
+ symmPerChannelQuantParams.channelDim),
+ });
+ },
+ [](const nn::Operand::ExtensionParams& extensionParams)
+ -> nn::GeneralResult<std::optional<OperandExtraParams>> {
+ return OperandExtraParams::make<OperandExtraParams::Tag::extension>(
+ extensionParams);
+ },
+ },
+ extraParams);
+}
+
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
+ return Operand{
+ .type = NN_TRY(unvalidatedConvert(operand.type)),
+ .dimensions = NN_TRY(toSigned(operand.dimensions)),
+ .scale = operand.scale,
+ .zeroPoint = operand.zeroPoint,
+ .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+ .location = NN_TRY(unvalidatedConvert(operand.location)),
+ .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
+ };
+}
+
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
+ return static_cast<OperationType>(operationType);
+}
+
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
+ return Operation{
+ .type = NN_TRY(unvalidatedConvert(operation.type)),
+ .inputs = NN_TRY(toSigned(operation.inputs)),
+ .outputs = NN_TRY(toSigned(operation.outputs)),
+ };
+}
+
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph) {
+ return Subgraph{
+ .operands = NN_TRY(unvalidatedConvert(subgraph.operands)),
+ .operations = NN_TRY(unvalidatedConvert(subgraph.operations)),
+ .inputIndexes = NN_TRY(toSigned(subgraph.inputIndexes)),
+ .outputIndexes = NN_TRY(toSigned(subgraph.outputIndexes)),
+ };
+}
+
+nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(
+ const nn::Model::OperandValues& operandValues) {
+ return std::vector<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
+}
+
+nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert(
+ const nn::Model::ExtensionNameAndPrefix& extensionNameToPrefix) {
+ return ExtensionNameAndPrefix{
+ .name = extensionNameToPrefix.name,
+ .prefix = extensionNameToPrefix.prefix,
+ };
+}
+
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
+ return Model{
+ .main = NN_TRY(unvalidatedConvert(model.main)),
+ .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+ .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+ .pools = NN_TRY(unvalidatedConvert(model.pools)),
+ .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+ .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
+ };
+}
+
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority) {
+ return static_cast<Priority>(priority);
+}
+
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
+ return Request{
+ .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+ .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+ .pools = NN_TRY(unvalidatedConvert(request.pools)),
+ };
+}
+
+nn::GeneralResult<RequestArgument> unvalidatedConvert(
+ const nn::Request::Argument& requestArgument) {
+ if (requestArgument.lifetime == nn::Request::Argument::LifeTime::POINTER) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
+ }
+ const bool hasNoValue = requestArgument.lifetime == nn::Request::Argument::LifeTime::NO_VALUE;
+ return RequestArgument{
+ .hasNoValue = hasNoValue,
+ .location = NN_TRY(unvalidatedConvert(requestArgument.location)),
+ .dimensions = NN_TRY(toSigned(requestArgument.dimensions)),
+ };
+}
+
+nn::GeneralResult<RequestMemoryPool> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool) {
+ return std::visit(
+ overloaded{
+ [](const nn::SharedMemory& memory) -> nn::GeneralResult<RequestMemoryPool> {
+ return RequestMemoryPool::make<RequestMemoryPool::Tag::pool>(
+ NN_TRY(unvalidatedConvert(memory)));
+ },
+ [](const nn::Request::MemoryDomainToken& token)
+ -> nn::GeneralResult<RequestMemoryPool> {
+ return RequestMemoryPool::make<RequestMemoryPool::Tag::token>(
+ underlyingType(token));
+ },
+ [](const nn::SharedBuffer& /*buffer*/) {
+ return (NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Unable to make memory pool from IBuffer")
+ .
+ operator nn::GeneralResult<RequestMemoryPool>();
+ },
+ },
+ memoryPool);
+}
+
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
+ return Timing{
+ .timeOnDevice = NN_TRY(unvalidatedConvert(timing.timeOnDevice)),
+ .timeInDriver = NN_TRY(unvalidatedConvert(timing.timeInDriver)),
+ };
+}
+
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::Duration& duration) {
+ const uint64_t nanoseconds = duration.count();
+ if (nanoseconds > std::numeric_limits<int64_t>::max()) {
+ return std::numeric_limits<int64_t>::max();
+ }
+ return static_cast<int64_t>(nanoseconds);
+}
+
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalDuration& optionalDuration) {
+ if (!optionalDuration.has_value()) {
+ return kNoTiming;
+ }
+ return unvalidatedConvert(optionalDuration.value());
+}
+
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalTimePoint& optionalTimePoint) {
+ if (!optionalTimePoint.has_value()) {
+ return kNoTiming;
+ }
+ return unvalidatedConvert(optionalTimePoint->time_since_epoch());
+}
+
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::SyncFence& syncFence) {
+ auto duplicatedFd = NN_TRY(nn::dupFd(syncFence.getFd()));
+ return ndk::ScopedFileDescriptor(duplicatedFd.release());
+}
+
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvertCache(
+ const nn::SharedHandle& handle) {
+ if (handle->ints.size() != 0) {
+ NN_ERROR() << "Cache handle must not contain ints";
+ }
+ if (handle->fds.size() != 1) {
+ NN_ERROR() << "Cache handle must contain exactly one fd but contains "
+ << handle->fds.size();
+ }
+ auto duplicatedFd = NN_TRY(nn::dupFd(handle->fds.front().get()));
+ return ndk::ScopedFileDescriptor(duplicatedFd.release());
+}
+
+nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken) {
+ return unvalidatedConvert(cacheToken);
+}
+
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
+ return validatedConvert(bufferDesc);
+}
+
+nn::GeneralResult<bool> convert(const nn::MeasureTiming& measureTiming) {
+ return validatedConvert(measureTiming);
+}
+
nn::GeneralResult<Memory> convert(const nn::SharedMemory& memory) {
return validatedConvert(memory);
}
@@ -715,11 +1023,62 @@
return validatedConvert(errorStatus);
}
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference) {
+ return validatedConvert(executionPreference);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+ return validatedConvert(model);
+}
+
+nn::GeneralResult<Priority> convert(const nn::Priority& priority) {
+ return validatedConvert(priority);
+}
+
+nn::GeneralResult<Request> convert(const nn::Request& request) {
+ return validatedConvert(request);
+}
+
+nn::GeneralResult<Timing> convert(const nn::Timing& timing) {
+ return validatedConvert(timing);
+}
+
+nn::GeneralResult<int64_t> convert(const nn::OptionalDuration& optionalDuration) {
+ return validatedConvert(optionalDuration);
+}
+
+nn::GeneralResult<int64_t> convert(const nn::OptionalTimePoint& outputShapes) {
+ return validatedConvert(outputShapes);
+}
+
+nn::GeneralResult<std::vector<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles) {
+ return validatedConvert(bufferRoles);
+}
+
nn::GeneralResult<std::vector<OutputShape>> convert(
const std::vector<nn::OutputShape>& outputShapes) {
return validatedConvert(outputShapes);
}
+nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
+ const std::vector<nn::SharedHandle>& cacheHandles) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(cacheHandles)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ std::vector<ndk::ScopedFileDescriptor> cacheFds;
+ cacheFds.reserve(cacheHandles.size());
+ for (const auto& cacheHandle : cacheHandles) {
+ cacheFds.push_back(NN_TRY(unvalidatedConvertCache(cacheHandle)));
+ }
+ return cacheFds;
+}
+
+nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
+ const std::vector<nn::SyncFence>& syncFences) {
+ return unvalidatedConvert(syncFences);
+}
+
nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& vec) {
if (!std::all_of(vec.begin(), vec.end(),
[](uint32_t v) { return v <= std::numeric_limits<int32_t>::max(); })) {
diff --git a/neuralnetworks/aidl/utils/src/Device.cpp b/neuralnetworks/aidl/utils/src/Device.cpp
new file mode 100644
index 0000000..02ca861
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Device.cpp
@@ -0,0 +1,294 @@
+/*
+ * 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 "Device.h"
+
+#include "Buffer.h"
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "ProtectCallback.h"
+#include "Utils.h"
+
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <any>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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 {
+
+namespace {
+
+nn::GeneralResult<std::vector<std::shared_ptr<IPreparedModel>>> convert(
+ const std::vector<nn::SharedPreparedModel>& preparedModels) {
+ std::vector<std::shared_ptr<IPreparedModel>> aidlPreparedModels(preparedModels.size());
+ for (size_t i = 0; i < preparedModels.size(); ++i) {
+ std::any underlyingResource = preparedModels[i]->getUnderlyingResource();
+ if (const auto* aidlPreparedModel =
+ std::any_cast<std::shared_ptr<aidl_hal::IPreparedModel>>(&underlyingResource)) {
+ aidlPreparedModels[i] = *aidlPreparedModel;
+ } else {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "Unable to convert from nn::IPreparedModel to aidl_hal::IPreparedModel";
+ }
+ }
+ return aidlPreparedModels;
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(IDevice* device) {
+ CHECK(device != nullptr);
+ Capabilities capabilities;
+ const auto ret = device->getCapabilities(&capabilities);
+ HANDLE_ASTATUS(ret) << "getCapabilities failed";
+ return nn::convert(capabilities);
+}
+
+nn::GeneralResult<std::string> getVersionStringFrom(aidl_hal::IDevice* device) {
+ CHECK(device != nullptr);
+ std::string version;
+ const auto ret = device->getVersionString(&version);
+ HANDLE_ASTATUS(ret) << "getVersionString failed";
+ return version;
+}
+
+nn::GeneralResult<nn::DeviceType> getDeviceTypeFrom(aidl_hal::IDevice* device) {
+ CHECK(device != nullptr);
+ DeviceType deviceType;
+ const auto ret = device->getType(&deviceType);
+ HANDLE_ASTATUS(ret) << "getDeviceType failed";
+ return nn::convert(deviceType);
+}
+
+nn::GeneralResult<std::vector<nn::Extension>> getSupportedExtensionsFrom(
+ aidl_hal::IDevice* device) {
+ CHECK(device != nullptr);
+ std::vector<Extension> supportedExtensions;
+ const auto ret = device->getSupportedExtensions(&supportedExtensions);
+ HANDLE_ASTATUS(ret) << "getExtensions failed";
+ return nn::convert(supportedExtensions);
+}
+
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom(
+ aidl_hal::IDevice* device) {
+ CHECK(device != nullptr);
+ NumberOfCacheFiles numberOfCacheFiles;
+ const auto ret = device->getNumberOfCacheFilesNeeded(&numberOfCacheFiles);
+ HANDLE_ASTATUS(ret) << "getNumberOfCacheFilesNeeded failed";
+
+ if (numberOfCacheFiles.numDataCache < 0 || numberOfCacheFiles.numModelCache < 0) {
+ return NN_ERROR() << "Driver reported negative numer of cache files needed";
+ }
+ if (static_cast<uint32_t>(numberOfCacheFiles.numModelCache) > nn::kMaxNumberOfCacheFiles) {
+ return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numModelCache files greater "
+ "than allowed max ("
+ << numberOfCacheFiles.numModelCache << " vs "
+ << nn::kMaxNumberOfCacheFiles << ")";
+ }
+ if (static_cast<uint32_t>(numberOfCacheFiles.numDataCache) > nn::kMaxNumberOfCacheFiles) {
+ return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numDataCache files greater "
+ "than allowed max ("
+ << numberOfCacheFiles.numDataCache << " vs " << nn::kMaxNumberOfCacheFiles
+ << ")";
+ }
+ return std::make_pair(numberOfCacheFiles.numDataCache, numberOfCacheFiles.numModelCache);
+}
+
+} // namespace
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(
+ std::string name, std::shared_ptr<aidl_hal::IDevice> device) {
+ if (name.empty()) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "aidl_hal::utils::Device::create must have non-empty name";
+ }
+ if (device == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "aidl_hal::utils::Device::create must have non-null device";
+ }
+
+ auto versionString = NN_TRY(getVersionStringFrom(device.get()));
+ const auto deviceType = NN_TRY(getDeviceTypeFrom(device.get()));
+ auto extensions = NN_TRY(getSupportedExtensionsFrom(device.get()));
+ auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
+ const auto numberOfCacheFilesNeeded = NN_TRY(getNumberOfCacheFilesNeededFrom(device.get()));
+
+ auto deathHandler = NN_TRY(DeathHandler::create(device));
+ return std::make_shared<const Device>(
+ PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
+ std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+ std::move(device), std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
+ nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+ nn::Capabilities capabilities,
+ std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+ std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler)
+ : kName(std::move(name)),
+ kVersionString(std::move(versionString)),
+ kDeviceType(deviceType),
+ kExtensions(std::move(extensions)),
+ kCapabilities(std::move(capabilities)),
+ kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded),
+ kDevice(std::move(device)),
+ kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+ return kName;
+}
+
+const std::string& Device::getVersionString() const {
+ return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+ return nn::Version::ANDROID_S;
+}
+
+nn::DeviceType Device::getType() const {
+ return kDeviceType;
+}
+
+bool Device::isUpdatable() const {
+ return false;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+ return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+ return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+ return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> Device::wait() const {
+ const auto ret = ndk::ScopedAStatus::fromStatus(AIBinder_ping(kDevice->asBinder().get()));
+ HANDLE_ASTATUS(ret) << "ping failed";
+ return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+ // Ensure that model is ready for IPC.
+ std::optional<nn::Model> maybeModelInShared;
+ const nn::Model& modelInShared =
+ NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+ const auto aidlModel = NN_TRY(convert(modelInShared));
+
+ std::vector<bool> supportedOperations;
+ const auto ret = kDevice->getSupportedOperations(aidlModel, &supportedOperations);
+ HANDLE_ASTATUS(ret) << "getSupportedOperations failed";
+
+ return supportedOperations;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+ const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+ nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+ const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+ // Ensure that model is ready for IPC.
+ std::optional<nn::Model> maybeModelInShared;
+ const nn::Model& modelInShared =
+ NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+ const auto aidlModel = NN_TRY(convert(modelInShared));
+ const auto aidlPreference = NN_TRY(convert(preference));
+ const auto aidlPriority = NN_TRY(convert(priority));
+ const auto aidlDeadline = NN_TRY(convert(deadline));
+ const auto aidlModelCache = NN_TRY(convert(modelCache));
+ const auto aidlDataCache = NN_TRY(convert(dataCache));
+ const auto aidlToken = NN_TRY(convert(token));
+
+ const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>();
+ const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+ const auto ret = kDevice->prepareModel(aidlModel, aidlPreference, aidlPriority, aidlDeadline,
+ aidlModelCache, aidlDataCache, aidlToken, cb);
+ HANDLE_ASTATUS(ret) << "prepareModel failed";
+
+ return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+ nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+ const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+ const auto aidlDeadline = NN_TRY(convert(deadline));
+ const auto aidlModelCache = NN_TRY(convert(modelCache));
+ const auto aidlDataCache = NN_TRY(convert(dataCache));
+ const auto aidlToken = NN_TRY(convert(token));
+
+ const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>();
+ const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+ const auto ret = kDevice->prepareModelFromCache(aidlDeadline, aidlModelCache, aidlDataCache,
+ aidlToken, cb);
+ HANDLE_ASTATUS(ret) << "prepareModelFromCache failed";
+
+ return cb->get();
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+ const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+ const std::vector<nn::BufferRole>& inputRoles,
+ const std::vector<nn::BufferRole>& outputRoles) const {
+ const auto aidlDesc = NN_TRY(convert(desc));
+ const auto aidlPreparedModels = NN_TRY(convert(preparedModels));
+ const auto aidlInputRoles = NN_TRY(convert(inputRoles));
+ const auto aidlOutputRoles = NN_TRY(convert(outputRoles));
+
+ std::vector<IPreparedModelParcel> aidlPreparedModelParcels;
+ aidlPreparedModelParcels.reserve(aidlPreparedModels.size());
+ for (const auto& preparedModel : aidlPreparedModels) {
+ aidlPreparedModelParcels.push_back({.preparedModel = preparedModel});
+ }
+
+ DeviceBuffer buffer;
+ const auto ret = kDevice->allocate(aidlDesc, aidlPreparedModelParcels, aidlInputRoles,
+ aidlOutputRoles, &buffer);
+ HANDLE_ASTATUS(ret) << "IDevice::allocate failed";
+
+ if (buffer.token < 0) {
+ return NN_ERROR() << "IDevice::allocate returned negative token";
+ }
+
+ return Buffer::create(buffer.buffer, static_cast<nn::Request::MemoryDomainToken>(buffer.token));
+}
+
+DeathMonitor* Device::getDeathMonitor() const {
+ return kDeathHandler.getDeathMonitor().get();
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..aee4d90
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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 "PreparedModel.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "ProtectCallback.h"
+#include "Utils.h"
+
+#include <android/binder_auto_utils.h>
+#include <nnapi/IPreparedModel.h>
+#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>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// 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 {
+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)));
+}
+
+nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionResults(
+ ErrorStatus status, const aidl_hal::Timing& timingLaunched,
+ const aidl_hal::Timing& timingFenced) {
+ HANDLE_HAL_STATUS(status) << "fenced execution callback info failed with " << toString(status);
+ return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
+}
+
+} // namespace
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+ std::shared_ptr<aidl_hal::IPreparedModel> preparedModel) {
+ if (preparedModel == nullptr) {
+ return NN_ERROR()
+ << "aidl_hal::utils::PreparedModel::create must have non-null preparedModel";
+ }
+
+ return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/,
+ std::shared_ptr<aidl_hal::IPreparedModel> preparedModel)
+ : kPreparedModel(std::move(preparedModel)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ // 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)));
+
+ ExecutionResult executionResult;
+ const auto ret = kPreparedModel->executeSynchronously(
+ aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration, &executionResult);
+ HANDLE_ASTATUS(ret) << "executeSynchronously 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);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+ nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const {
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ const nn::Request& requestInShared =
+ NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
+
+ const auto aidlRequest = NN_TRY(convert(requestInShared));
+ const auto aidlWaitFor = NN_TRY(convert(waitFor));
+ const auto aidlMeasure = NN_TRY(convert(measure));
+ const auto aidlDeadline = NN_TRY(convert(deadline));
+ const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+ const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+
+ FencedExecutionResult result;
+ const auto ret = kPreparedModel->executeFenced(aidlRequest, aidlWaitFor, aidlMeasure,
+ aidlDeadline, aidlLoopTimeoutDuration,
+ aidlTimeoutDurationAfterFence, &result);
+ HANDLE_ASTATUS(ret) << "executeFenced failed";
+
+ auto resultSyncFence = nn::SyncFence::createAsSignaled();
+ if (result.syncFence.get() != -1) {
+ resultSyncFence = NN_TRY(nn::convert(result.syncFence));
+ }
+
+ auto callback = result.callback;
+ if (callback == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "callback is null";
+ }
+
+ // If executeFenced required the request memory to be moved into shared memory, block here until
+ // the fenced execution has completed and flush the memory back.
+ if (maybeRequestInShared.has_value()) {
+ const auto state = resultSyncFence.syncWait({});
+ if (state != nn::SyncFence::FenceState::SIGNALED) {
+ return NN_ERROR() << "syncWait failed with " << state;
+ }
+ NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
+ }
+
+ // Create callback which can be used to retrieve the execution error status and timings.
+ nn::ExecuteFencedInfoCallback resultCallback =
+ [callback]() -> nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> {
+ ErrorStatus errorStatus;
+ Timing timingLaunched;
+ Timing timingFenced;
+ const auto ret = callback->getExecutionInfo(&timingLaunched, &timingFenced, &errorStatus);
+ HANDLE_ASTATUS(ret) << "fenced execution callback getExecutionInfo failed";
+ return convertFencedExecutionResults(errorStatus, timingLaunched, timingFenced);
+ };
+
+ return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
+}
+
+nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
+ return hal::V1_0::utils::Burst::create(shared_from_this());
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+ std::shared_ptr<aidl_hal::IPreparedModel> resource = kPreparedModel;
+ return resource;
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/ProtectCallback.cpp b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
new file mode 100644
index 0000000..124641c
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "ProtectCallback.h"
+
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <nnapi/Result.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "Utils.h"
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+void DeathMonitor::serviceDied() {
+ std::lock_guard guard(mMutex);
+ std::for_each(mObjects.begin(), mObjects.end(),
+ [](hal::utils::IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
+}
+
+void DeathMonitor::serviceDied(void* cookie) {
+ auto deathMonitor = static_cast<DeathMonitor*>(cookie);
+ deathMonitor->serviceDied();
+}
+
+void DeathMonitor::add(hal::utils::IProtectedCallback* killable) const {
+ CHECK(killable != nullptr);
+ std::lock_guard guard(mMutex);
+ mObjects.push_back(killable);
+}
+
+void DeathMonitor::remove(hal::utils::IProtectedCallback* killable) const {
+ CHECK(killable != nullptr);
+ std::lock_guard guard(mMutex);
+ const auto removedIter = std::remove(mObjects.begin(), mObjects.end(), killable);
+ mObjects.erase(removedIter);
+}
+
+nn::GeneralResult<DeathHandler> DeathHandler::create(std::shared_ptr<ndk::ICInterface> object) {
+ if (object == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "utils::DeathHandler::create must have non-null object";
+ }
+ auto deathMonitor = std::make_shared<DeathMonitor>();
+ auto deathRecipient = ndk::ScopedAIBinder_DeathRecipient(
+ AIBinder_DeathRecipient_new(DeathMonitor::serviceDied));
+
+ // If passed a local binder, AIBinder_linkToDeath will do nothing and return
+ // STATUS_INVALID_OPERATION. We ignore this case because we only use local binders in tests
+ // where this is not an error.
+ if (object->isRemote()) {
+ const auto ret = ndk::ScopedAStatus::fromStatus(AIBinder_linkToDeath(
+ object->asBinder().get(), deathRecipient.get(), deathMonitor.get()));
+ HANDLE_ASTATUS(ret) << "AIBinder_linkToDeath failed";
+ }
+
+ return DeathHandler(std::move(object), std::move(deathRecipient), std::move(deathMonitor));
+}
+
+DeathHandler::DeathHandler(std::shared_ptr<ndk::ICInterface> object,
+ ndk::ScopedAIBinder_DeathRecipient deathRecipient,
+ std::shared_ptr<DeathMonitor> deathMonitor)
+ : kObject(std::move(object)),
+ kDeathRecipient(std::move(deathRecipient)),
+ kDeathMonitor(std::move(deathMonitor)) {
+ CHECK(kObject != nullptr);
+ CHECK(kDeathRecipient.get() != nullptr);
+ CHECK(kDeathMonitor != nullptr);
+}
+
+DeathHandler::~DeathHandler() {
+ if (kObject != nullptr && kDeathRecipient.get() != nullptr && kDeathMonitor != nullptr) {
+ const auto ret = ndk::ScopedAStatus::fromStatus(AIBinder_unlinkToDeath(
+ kObject->asBinder().get(), kDeathRecipient.get(), kDeathMonitor.get()));
+ const auto maybeSuccess = handleTransportError(ret);
+ if (!maybeSuccess.ok()) {
+ LOG(ERROR) << maybeSuccess.error().message;
+ }
+ }
+}
+
+[[nodiscard]] ::android::base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
+ hal::utils::IProtectedCallback* killable) const {
+ CHECK(killable != nullptr);
+ kDeathMonitor->add(killable);
+ return ::android::base::make_scope_guard(
+ [deathMonitor = kDeathMonitor, killable] { deathMonitor->remove(killable); });
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Service.cpp b/neuralnetworks/aidl/utils/src/Service.cpp
new file mode 100644
index 0000000..511de55
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Service.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "Service.h"
+
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+
+#include "Device.h"
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& instanceName) {
+ auto fullName = std::string(IDevice::descriptor) + "/" + instanceName;
+ hal::utils::ResilientDevice::Factory makeDevice =
+ [instanceName,
+ name = std::move(fullName)](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+ const auto& getService =
+ blocking ? AServiceManager_getService : AServiceManager_checkService;
+ auto service = IDevice::fromBinder(ndk::SpAIBinder(getService(name.c_str())));
+ if (service == nullptr) {
+ return NN_ERROR() << (blocking ? "AServiceManager_getService"
+ : "AServiceManager_checkService")
+ << " returned nullptr";
+ }
+ ABinderProcess_startThreadPool();
+ return Device::create(instanceName, std::move(service));
+ };
+
+ return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Utils.cpp b/neuralnetworks/aidl/utils/src/Utils.cpp
index 8d00e59..95516c8 100644
--- a/neuralnetworks/aidl/utils/src/Utils.cpp
+++ b/neuralnetworks/aidl/utils/src/Utils.cpp
@@ -16,13 +16,12 @@
#include "Utils.h"
+#include <android/binder_status.h>
#include <nnapi/Result.h>
namespace aidl::android::hardware::neuralnetworks::utils {
namespace {
-using ::android::nn::GeneralResult;
-
template <typename Type>
nn::GeneralResult<std::vector<Type>> cloneVec(const std::vector<Type>& arguments) {
std::vector<Type> clonedObjects;
@@ -34,13 +33,13 @@
}
template <typename Type>
-GeneralResult<std::vector<Type>> clone(const std::vector<Type>& arguments) {
+nn::GeneralResult<std::vector<Type>> clone(const std::vector<Type>& arguments) {
return cloneVec(arguments);
}
} // namespace
-GeneralResult<Memory> clone(const Memory& memory) {
+nn::GeneralResult<Memory> clone(const Memory& memory) {
common::NativeHandle nativeHandle;
nativeHandle.ints = memory.handle.ints;
nativeHandle.fds.reserve(memory.handle.fds.size());
@@ -58,7 +57,7 @@
};
}
-GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool) {
+nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool) {
using Tag = RequestMemoryPool::Tag;
switch (requestPool.getTag()) {
case Tag::pool:
@@ -70,10 +69,10 @@
// compiler.
return (NN_ERROR() << "Unrecognized request pool tag: " << requestPool.getTag())
.
- operator GeneralResult<RequestMemoryPool>();
+ operator nn::GeneralResult<RequestMemoryPool>();
}
-GeneralResult<Request> clone(const Request& request) {
+nn::GeneralResult<Request> clone(const Request& request) {
return Request{
.inputs = request.inputs,
.outputs = request.outputs,
@@ -81,7 +80,7 @@
};
}
-GeneralResult<Model> clone(const Model& model) {
+nn::GeneralResult<Model> clone(const Model& model) {
return Model{
.main = model.main,
.referenced = model.referenced,
@@ -92,4 +91,20 @@
};
}
+nn::GeneralResult<void> handleTransportError(const ndk::ScopedAStatus& ret) {
+ if (ret.getStatus() == STATUS_DEAD_OBJECT) {
+ return nn::error(nn::ErrorStatus::DEAD_OBJECT)
+ << "Binder transaction returned STATUS_DEAD_OBJECT: " << ret.getDescription();
+ }
+ if (ret.isOk()) {
+ return {};
+ }
+ if (ret.getExceptionCode() != EX_SERVICE_SPECIFIC) {
+ return nn::error(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Binder transaction returned exception: " << ret.getDescription();
+ }
+ return nn::error(static_cast<nn::ErrorStatus>(ret.getServiceSpecificError()))
+ << ret.getMessage();
+}
+
} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/test/BufferTest.cpp b/neuralnetworks/aidl/utils/test/BufferTest.cpp
new file mode 100644
index 0000000..9736160
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/BufferTest.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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 "MockBuffer.h"
+
+#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
+#include <aidl/android/hardware/neuralnetworks/IBuffer.h>
+#include <android/binder_auto_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/aidl/Buffer.h>
+
+#include <functional>
+#include <memory>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+const auto kMemory = nn::createSharedMemory(4).value();
+const std::shared_ptr<IBuffer> kInvalidBuffer;
+constexpr auto kInvalidToken = nn::Request::MemoryDomainToken{0};
+constexpr auto kToken = nn::Request::MemoryDomainToken{1};
+
+constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
+
+constexpr auto makeGeneralFailure = [] {
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+};
+constexpr auto makeGeneralTransportFailure = [] {
+ return ndk::ScopedAStatus::fromStatus(STATUS_NO_MEMORY);
+};
+constexpr auto makeDeadObjectFailure = [] {
+ return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
+};
+
+} // namespace
+
+TEST(BufferTest, invalidBuffer) {
+ // run test
+ const auto result = Buffer::create(kInvalidBuffer, kToken);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, invalidToken) {
+ // setup call
+ const auto mockBuffer = MockBuffer::create();
+
+ // run test
+ const auto result = Buffer::create(mockBuffer, kInvalidToken);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, create) {
+ // setup call
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+
+ // run test
+ const auto token = buffer->getToken();
+
+ // verify result
+ EXPECT_EQ(token, kToken);
+}
+
+TEST(BufferTest, copyTo) {
+ // setup call
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+ EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeStatusOk));
+
+ // run test
+ const auto result = buffer->copyTo(kMemory);
+
+ // verify result
+ EXPECT_TRUE(result.has_value()) << result.error().message;
+}
+
+TEST(BufferTest, copyToError) {
+ // setup test
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+ EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = buffer->copyTo(kMemory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyToTransportFailure) {
+ // setup test
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+ EXPECT_CALL(*mockBuffer, copyTo(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = buffer->copyTo(kMemory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyToDeadObject) {
+ // setup test
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+ EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = buffer->copyTo(kMemory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(BufferTest, copyFrom) {
+ // setup call
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+ EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(InvokeWithoutArgs(makeStatusOk));
+
+ // run test
+ const auto result = buffer->copyFrom(kMemory, {});
+
+ // verify result
+ EXPECT_TRUE(result.has_value());
+}
+
+TEST(BufferTest, copyFromError) {
+ // setup test
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+ EXPECT_CALL(*mockBuffer, copyFrom(_, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = buffer->copyFrom(kMemory, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyFromTransportFailure) {
+ // setup test
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+ EXPECT_CALL(*mockBuffer, copyFrom(_, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = buffer->copyFrom(kMemory, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyFromDeadObject) {
+ // setup test
+ const auto mockBuffer = MockBuffer::create();
+ const auto buffer = Buffer::create(mockBuffer, kToken).value();
+ EXPECT_CALL(*mockBuffer, copyFrom(_, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = buffer->copyFrom(kMemory, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/test/DeviceTest.cpp b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..e53b0a8
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
@@ -0,0 +1,861 @@
+/*
+ * 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 "MockBuffer.h"
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_status.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/aidl/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+namespace nn = ::android::nn;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::SetArgPointee;
+
+const nn::Model kSimpleModel = {
+ .main = {.operands = {{.type = nn::OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .lifetime = nn::Operand::LifeTime::SUBGRAPH_INPUT},
+ {.type = nn::OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .lifetime = nn::Operand::LifeTime::SUBGRAPH_OUTPUT}},
+ .operations = {{.type = nn::OperationType::RELU, .inputs = {0}, .outputs = {1}}},
+ .inputIndexes = {0},
+ .outputIndexes = {1}}};
+
+const std::string kName = "Google-MockV1";
+const std::string kInvalidName = "";
+const std::shared_ptr<BnDevice> kInvalidDevice;
+constexpr PerformanceInfo kNoPerformanceInfo = {.execTime = std::numeric_limits<float>::max(),
+ .powerUsage = std::numeric_limits<float>::max()};
+constexpr NumberOfCacheFiles kNumberOfCacheFiles = {.numModelCache = nn::kMaxNumberOfCacheFiles,
+ .numDataCache = nn::kMaxNumberOfCacheFiles};
+
+constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
+
+std::shared_ptr<MockDevice> createMockDevice() {
+ const auto mockDevice = MockDevice::create();
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, getVersionString(_))
+ .WillByDefault(DoAll(SetArgPointee<0>(kName), InvokeWithoutArgs(makeStatusOk)));
+ ON_CALL(*mockDevice, getType(_))
+ .WillByDefault(
+ DoAll(SetArgPointee<0>(DeviceType::OTHER), InvokeWithoutArgs(makeStatusOk)));
+ ON_CALL(*mockDevice, getSupportedExtensions(_))
+ .WillByDefault(DoAll(SetArgPointee<0>(std::vector<Extension>{}),
+ InvokeWithoutArgs(makeStatusOk)));
+ ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+ .WillByDefault(
+ DoAll(SetArgPointee<0>(kNumberOfCacheFiles), InvokeWithoutArgs(makeStatusOk)));
+ ON_CALL(*mockDevice, getCapabilities(_))
+ .WillByDefault(
+ DoAll(SetArgPointee<0>(Capabilities{
+ .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+ .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+ .ifPerformance = kNoPerformanceInfo,
+ .whilePerformance = kNoPerformanceInfo,
+ }),
+ InvokeWithoutArgs(makeStatusOk)));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockDevice, getVersionString(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getType(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+constexpr auto makePreparedModelReturnImpl =
+ [](ErrorStatus launchStatus, ErrorStatus returnStatus,
+ const std::shared_ptr<MockPreparedModel>& preparedModel,
+ const std::shared_ptr<IPreparedModelCallback>& cb) {
+ cb->notify(returnStatus, preparedModel);
+ if (launchStatus == ErrorStatus::NONE) {
+ return ndk::ScopedAStatus::ok();
+ }
+ return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(launchStatus));
+ };
+
+auto makePreparedModelReturn(ErrorStatus launchStatus, ErrorStatus returnStatus,
+ const std::shared_ptr<MockPreparedModel>& preparedModel) {
+ return [launchStatus, returnStatus, preparedModel](
+ const Model& /*model*/, ExecutionPreference /*preference*/,
+ Priority /*priority*/, const int64_t& /*deadline*/,
+ const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
+ const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
+ const std::vector<uint8_t>& /*token*/,
+ const std::shared_ptr<IPreparedModelCallback>& cb) -> ndk::ScopedAStatus {
+ return makePreparedModelReturnImpl(launchStatus, returnStatus, preparedModel, cb);
+ };
+}
+
+auto makePreparedModelFromCacheReturn(ErrorStatus launchStatus, ErrorStatus returnStatus,
+ const std::shared_ptr<MockPreparedModel>& preparedModel) {
+ return [launchStatus, returnStatus, preparedModel](
+ const int64_t& /*deadline*/,
+ const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
+ const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
+ const std::vector<uint8_t>& /*token*/,
+ const std::shared_ptr<IPreparedModelCallback>& cb) {
+ return makePreparedModelReturnImpl(launchStatus, returnStatus, preparedModel, cb);
+ };
+}
+
+constexpr auto makeGeneralFailure = [] {
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+};
+constexpr auto makeGeneralTransportFailure = [] {
+ return ndk::ScopedAStatus::fromStatus(STATUS_NO_MEMORY);
+};
+constexpr auto makeDeadObjectFailure = [] {
+ return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
+};
+
+} // namespace
+
+TEST(DeviceTest, invalidName) {
+ // run test
+ const auto device = MockDevice::create();
+ const auto result = Device::create(kInvalidName, device);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, invalidDevice) {
+ // run test
+ const auto result = Device::create(kName, kInvalidDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, getVersionStringError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getVersionString(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getVersionStringTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getVersionString(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getVersionStringDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getVersionString(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getTypeError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getType(_)).Times(1).WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getTypeTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getType(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getTypeDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getType(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedExtensionsError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedExtensionsTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedExtensionsDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, dataCacheFilesExceedsSpecifiedMax) {
+ // setup test
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<0>(NumberOfCacheFiles{
+ .numModelCache = nn::kMaxNumberOfCacheFiles + 1,
+ .numDataCache = nn::kMaxNumberOfCacheFiles}),
+ InvokeWithoutArgs(makeStatusOk)));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, modelCacheFilesExceedsSpecifiedMax) {
+ // setup test
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<0>(NumberOfCacheFiles{
+ .numModelCache = nn::kMaxNumberOfCacheFiles,
+ .numDataCache = nn::kMaxNumberOfCacheFiles + 1}),
+ InvokeWithoutArgs(makeStatusOk)));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getCapabilitiesError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getCapabilities(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getCapabilities(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getCapabilities(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = Device::create(kName, mockDevice);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getName) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto& name = device->getName();
+
+ // verify result
+ EXPECT_EQ(name, kName);
+}
+
+TEST(DeviceTest, getFeatureLevel) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto featureLevel = device->getFeatureLevel();
+
+ // verify result
+ EXPECT_EQ(featureLevel, nn::Version::ANDROID_S);
+}
+
+TEST(DeviceTest, getCachedData) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1);
+ EXPECT_CALL(*mockDevice, getType(_)).Times(1);
+ EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(1);
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
+ EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(1);
+
+ const auto result = Device::create(kName, mockDevice);
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ const auto& device = result.value();
+
+ // run test and verify results
+ EXPECT_EQ(device->getVersionString(), device->getVersionString());
+ EXPECT_EQ(device->getType(), device->getType());
+ EXPECT_EQ(device->getSupportedExtensions(), device->getSupportedExtensions());
+ EXPECT_EQ(device->getNumberOfCacheFilesNeeded(), device->getNumberOfCacheFilesNeeded());
+ EXPECT_EQ(device->getCapabilities(), device->getCapabilities());
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+ .Times(1)
+ .WillOnce(DoAll(
+ SetArgPointee<1>(std::vector<bool>(kSimpleModel.main.operations.size(), true)),
+ InvokeWithoutArgs(makeStatusOk)));
+
+ // run test
+ const auto result = device->getSupportedOperations(kSimpleModel);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ const auto& supportedOperations = result.value();
+ EXPECT_EQ(supportedOperations.size(), kSimpleModel.main.operations.size());
+ EXPECT_THAT(supportedOperations, Each(testing::IsTrue()));
+}
+
+TEST(DeviceTest, getSupportedOperationsError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = device->getSupportedOperations(kSimpleModel);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = device->getSupportedOperations(kSimpleModel);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = device->getSupportedOperations(kSimpleModel);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModel) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto mockPreparedModel = MockPreparedModel::create();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::NONE, ErrorStatus::NONE,
+ mockPreparedModel)));
+
+ // run test
+ const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+ nn::Priority::DEFAULT, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelLaunchError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::GENERAL_FAILURE,
+ ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+ // run test
+ const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+ nn::Priority::DEFAULT, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelReturnError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::NONE,
+ ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+ // run test
+ const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+ nn::Priority::DEFAULT, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelNullptrError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(
+ Invoke(makePreparedModelReturn(ErrorStatus::NONE, ErrorStatus::NONE, nullptr)));
+
+ // run test
+ const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+ nn::Priority::DEFAULT, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+ nn::Priority::DEFAULT, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+ nn::Priority::DEFAULT, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelAsyncCrash) {
+ // setup test
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto ret = [&device]() {
+ DeathMonitor::serviceDied(device->getDeathMonitor());
+ return ndk::ScopedAStatus::ok();
+ };
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(ret));
+
+ // run test
+ const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+ nn::Priority::DEFAULT, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCache) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto mockPreparedModel = MockPreparedModel::create();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelFromCacheReturn(ErrorStatus::NONE, ErrorStatus::NONE,
+ mockPreparedModel)));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelFromCacheLaunchError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+ ErrorStatus::GENERAL_FAILURE, ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheReturnError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+ ErrorStatus::NONE, ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheNullptrError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelFromCacheReturn(ErrorStatus::NONE, ErrorStatus::NONE,
+ nullptr)));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCacheAsyncCrash) {
+ // setup test
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto ret = [&device]() {
+ DeathMonitor::serviceDied(device->getDeathMonitor());
+ return ndk::ScopedAStatus::ok();
+ };
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(ret));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, allocate) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto mockBuffer = DeviceBuffer{.buffer = MockBuffer::create(), .token = 1};
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<4>(mockBuffer), InvokeWithoutArgs(makeStatusOk)));
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, allocateError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, allocateTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, allocateDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/test/MockBuffer.h b/neuralnetworks/aidl/utils/test/MockBuffer.h
new file mode 100644
index 0000000..5746176
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * 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_BUFFER
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER
+
+#include <aidl/android/hardware/neuralnetworks/BnBuffer.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 MockBuffer final : public BnBuffer {
+ public:
+ static std::shared_ptr<MockBuffer> create();
+
+ MOCK_METHOD(ndk::ScopedAStatus, copyTo, (const Memory& dst), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, copyFrom,
+ (const Memory& src, const std::vector<int32_t>& dimensions), (override));
+};
+
+inline std::shared_ptr<MockBuffer> MockBuffer::create() {
+ return ndk::SharedRefBase::make<MockBuffer>();
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER
diff --git a/neuralnetworks/aidl/utils/test/MockDevice.h b/neuralnetworks/aidl/utils/test/MockDevice.h
new file mode 100644
index 0000000..9b35bf8
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockDevice.h
@@ -0,0 +1,67 @@
+/*
+ * 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_DEVICE
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE
+
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockDevice final : public BnDevice {
+ public:
+ static std::shared_ptr<MockDevice> create();
+
+ MOCK_METHOD(ndk::ScopedAStatus, allocate,
+ (const BufferDesc& desc, const std::vector<IPreparedModelParcel>& preparedModels,
+ const std::vector<BufferRole>& inputRoles,
+ const std::vector<BufferRole>& outputRoles, DeviceBuffer* deviceBuffer),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getCapabilities, (Capabilities * capabilities), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getNumberOfCacheFilesNeeded,
+ (NumberOfCacheFiles * numberOfCacheFiles), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSupportedExtensions, (std::vector<Extension> * extensions),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSupportedOperations,
+ (const Model& model, std::vector<bool>* supportedOperations), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getType, (DeviceType * deviceType), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getVersionString, (std::string * version), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, prepareModel,
+ (const Model& model, ExecutionPreference preference, Priority priority,
+ int64_t deadline, const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+ const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+ const std::vector<uint8_t>& token,
+ const std::shared_ptr<IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, prepareModelFromCache,
+ (int64_t deadline, const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+ const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+ const std::vector<uint8_t>& token,
+ const std::shared_ptr<IPreparedModelCallback>& callback),
+ (override));
+};
+
+inline std::shared_ptr<MockDevice> MockDevice::create() {
+ return ndk::SharedRefBase::make<MockDevice>();
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE
diff --git a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
new file mode 100644
index 0000000..463e1c9
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
@@ -0,0 +1,45 @@
+/*
+ * 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_FENCED_EXECUTION_CALLBACK
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+
+#include <aidl/android/hardware/neuralnetworks/BnFencedExecutionCallback.h>
+#include <android/binder_auto_utils.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 MockFencedExecutionCallback final : public BnFencedExecutionCallback {
+ public:
+ static std::shared_ptr<MockFencedExecutionCallback> create();
+
+ // V1_3 methods below.
+ MOCK_METHOD(ndk::ScopedAStatus, getExecutionInfo,
+ (Timing * timingLaunched, Timing* timingFenced, ErrorStatus* errorStatus),
+ (override));
+};
+
+inline std::shared_ptr<MockFencedExecutionCallback> MockFencedExecutionCallback::create() {
+ return ndk::SharedRefBase::make<MockFencedExecutionCallback>();
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
diff --git a/neuralnetworks/aidl/utils/test/MockPreparedModel.h b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..36e0ec3
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
@@ -0,0 +1,52 @@
+/*
+ * 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_PREPARED_MODEL
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL
+
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockPreparedModel final : public BnPreparedModel {
+ public:
+ static std::shared_ptr<MockPreparedModel> create();
+
+ MOCK_METHOD(ndk::ScopedAStatus, executeSynchronously,
+ (const Request& request, bool measureTiming, int64_t deadline,
+ int64_t loopTimeoutDuration, ExecutionResult* executionResult),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, executeFenced,
+ (const Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor,
+ bool measureTiming, int64_t deadline, int64_t loopTimeoutDuration,
+ int64_t duration, FencedExecutionResult* fencedExecutionResult),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, configureExecutionBurst, (std::shared_ptr<IBurst> * burst),
+ (override));
+};
+
+inline std::shared_ptr<MockPreparedModel> MockPreparedModel::create() {
+ return ndk::SharedRefBase::make<MockPreparedModel>();
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL
diff --git a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
new file mode 100644
index 0000000..7e28861
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,272 @@
+/*
+ * 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 "MockFencedExecutionCallback.h"
+#include "MockPreparedModel.h"
+
+#include <aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/aidl/PreparedModel.h>
+
+#include <functional>
+#include <memory>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::SetArgPointee;
+
+const std::shared_ptr<IPreparedModel> kInvalidPreparedModel;
+constexpr auto kNoTiming = Timing{.timeOnDevice = -1, .timeInDriver = -1};
+
+constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
+
+constexpr auto makeGeneralFailure = [] {
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+};
+constexpr auto makeGeneralTransportFailure = [] {
+ return ndk::ScopedAStatus::fromStatus(STATUS_NO_MEMORY);
+};
+constexpr auto makeDeadObjectFailure = [] {
+ return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
+};
+
+auto makeFencedExecutionResult(const std::shared_ptr<MockFencedExecutionCallback>& callback) {
+ return [callback](const Request& /*request*/,
+ const std::vector<ndk::ScopedFileDescriptor>& /*waitFor*/,
+ bool /*measureTiming*/, int64_t /*deadline*/, int64_t /*loopTimeoutDuration*/,
+ int64_t /*duration*/, FencedExecutionResult* fencedExecutionResult) {
+ *fencedExecutionResult = FencedExecutionResult{.callback = callback,
+ .syncFence = ndk::ScopedFileDescriptor(-1)};
+ return ndk::ScopedAStatus::ok();
+ };
+}
+
+} // namespace
+
+TEST(PreparedModelTest, invalidPreparedModel) {
+ // run test
+ const auto result = PreparedModel::create(kInvalidPreparedModel);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSync) {
+ // setup call
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ const auto mockExecutionResult = ExecutionResult{
+ .outputSufficientSize = true,
+ .outputShapes = {},
+ .timing = kNoTiming,
+ };
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(
+ DoAll(SetArgPointee<4>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk)));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ EXPECT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeSyncError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeGeneralFailure));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSyncTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSyncDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeFenced) {
+ // setup call
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ const auto mockCallback = MockFencedExecutionCallback::create();
+ EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+ SetArgPointee<2>(ErrorStatus::NONE), Invoke(makeStatusOk)));
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ const auto& [syncFence, callback] = result.value();
+ EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+ ASSERT_NE(callback, nullptr);
+
+ // get results from callback
+ const auto callbackResult = callback();
+ ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code << ": "
+ << callbackResult.error().message;
+}
+
+TEST(PreparedModelTest, executeFencedCallbackError) {
+ // setup call
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ const auto mockCallback = MockFencedExecutionCallback::create();
+ EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+ SetArgPointee<2>(ErrorStatus::GENERAL_FAILURE),
+ Invoke(makeStatusOk))));
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ const auto& [syncFence, callback] = result.value();
+ EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+ ASSERT_NE(callback, nullptr);
+
+ // verify callback failure
+ const auto callbackResult = callback();
+ ASSERT_FALSE(callbackResult.has_value());
+ EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+
+TEST(PreparedModelTest, getUnderlyingResource) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto resource = preparedModel->getUnderlyingResource();
+
+ // verify resource
+ const std::shared_ptr<IPreparedModel>* maybeMock =
+ std::any_cast<std::shared_ptr<IPreparedModel>>(&resource);
+ ASSERT_NE(maybeMock, nullptr);
+ EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index 4eb704b..1440429 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -17,6 +17,7 @@
#include "GeneratedTestHarness.h"
#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
+#include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <android/sync.h>
@@ -101,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};
@@ -299,9 +300,11 @@
}
static void makeOutputInsufficientSize(uint32_t outputIndex, Request* request) {
- auto& length = request->outputs[outputIndex].location.length;
- ASSERT_GT(length, 1u);
- length -= 1u;
+ auto& loc = request->outputs[outputIndex].location;
+ ASSERT_GT(loc.length, 1u);
+ loc.length -= 1u;
+ // Test that the padding is not used for output data.
+ loc.padding += 1u;
}
static void makeOutputDimensionsUnspecified(Model* model) {
@@ -336,6 +339,12 @@
std::vector<std::shared_ptr<IBuffer>> mBuffers;
};
+// Returns the number of bytes needed to round up "size" to the nearest multiple of "multiple".
+static uint32_t roundUpBytesNeeded(uint32_t size, uint32_t multiple) {
+ CHECK(multiple != 0);
+ return ((size + multiple - 1) / multiple) * multiple - size;
+}
+
std::optional<Request> ExecutionContext::createRequest(const TestModel& testModel,
MemoryType memoryType) {
// Memory pools are organized as:
@@ -370,10 +379,13 @@
}
// Reserve shared memory for input.
+ inputSize += roundUpBytesNeeded(inputSize, nn::kDefaultRequestMemoryAlignment);
+ const auto padding = roundUpBytesNeeded(op.data.size(), nn::kDefaultRequestMemoryPadding);
DataLocation loc = {.poolIndex = kInputPoolIndex,
.offset = static_cast<int64_t>(inputSize),
- .length = static_cast<int64_t>(op.data.size())};
- inputSize += op.data.alignedSize();
+ .length = static_cast<int64_t>(op.data.size()),
+ .padding = static_cast<int64_t>(padding)};
+ inputSize += (op.data.size() + padding);
inputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
}
@@ -404,10 +416,13 @@
size_t bufferSize = std::max<size_t>(op.data.size(), 1);
// Reserve shared memory for output.
+ outputSize += roundUpBytesNeeded(outputSize, nn::kDefaultRequestMemoryAlignment);
+ const auto padding = roundUpBytesNeeded(bufferSize, nn::kDefaultRequestMemoryPadding);
DataLocation loc = {.poolIndex = kOutputPoolIndex,
.offset = static_cast<int64_t>(outputSize),
- .length = static_cast<int64_t>(bufferSize)};
- outputSize += op.data.size() == 0 ? TestBuffer::kAlignment : op.data.alignedSize();
+ .length = static_cast<int64_t>(bufferSize),
+ .padding = static_cast<int64_t>(padding)};
+ outputSize += (bufferSize + padding);
outputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
}
@@ -568,6 +583,53 @@
}
break;
}
+ case Executor::BURST: {
+ SCOPED_TRACE("burst");
+
+ // create burst
+ std::shared_ptr<IBurst> burst;
+ auto ret = preparedModel->configureExecutionBurst(&burst);
+ ASSERT_TRUE(ret.isOk()) << ret.getDescription();
+ ASSERT_NE(nullptr, burst.get());
+
+ // associate a unique slot with each memory pool
+ int64_t currentSlot = 0;
+ std::vector<int64_t> slots;
+ slots.reserve(request.pools.size());
+ for (const auto& pool : request.pools) {
+ if (pool.getTag() == RequestMemoryPool::Tag::pool) {
+ slots.push_back(currentSlot++);
+ } else {
+ EXPECT_EQ(pool.getTag(), RequestMemoryPool::Tag::token);
+ slots.push_back(-1);
+ }
+ }
+
+ ExecutionResult executionResult;
+ // execute
+ ret = burst->executeSynchronously(request, slots, testConfig.measureTiming, kNoDeadline,
+ loopTimeoutDuration, &executionResult);
+ ASSERT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
+ << ret.getDescription();
+ if (ret.isOk()) {
+ executionStatus = executionResult.outputSufficientSize
+ ? ErrorStatus::NONE
+ : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+ outputShapes = std::move(executionResult.outputShapes);
+ timing = executionResult.timing;
+ } else {
+ executionStatus = static_cast<ErrorStatus>(ret.getServiceSpecificError());
+ }
+
+ // Mark each slot as unused after the execution. This is unnecessary because the burst
+ // is freed after this scope ends, but this is here to test the functionality.
+ for (int64_t slot : slots) {
+ ret = burst->releaseMemoryResource(slot);
+ ASSERT_TRUE(ret.isOk()) << ret.getDescription();
+ }
+
+ break;
+ }
case Executor::FENCED: {
SCOPED_TRACE("fenced");
ErrorStatus result = ErrorStatus::NONE;
@@ -713,19 +775,19 @@
case TestKind::GENERAL: {
outputTypesList = {OutputType::FULLY_SPECIFIED};
measureTimingList = {false, true};
- executorList = {Executor::SYNC};
+ executorList = {Executor::SYNC, Executor::BURST};
memoryTypeList = {MemoryType::ASHMEM};
} break;
case TestKind::DYNAMIC_SHAPE: {
outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT};
measureTimingList = {false, true};
- executorList = {Executor::SYNC, Executor::FENCED};
+ executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
memoryTypeList = {MemoryType::ASHMEM};
} break;
case TestKind::MEMORY_DOMAIN: {
outputTypesList = {OutputType::FULLY_SPECIFIED};
measureTimingList = {false};
- executorList = {Executor::SYNC, Executor::FENCED};
+ executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
memoryTypeList = {MemoryType::BLOB_AHWB, MemoryType::DEVICE};
} break;
case TestKind::FENCED_COMPUTE: {
@@ -741,7 +803,7 @@
case TestKind::INTINITE_LOOP_TIMEOUT: {
outputTypesList = {OutputType::MISSED_DEADLINE};
measureTimingList = {false, true};
- executorList = {Executor::SYNC, Executor::FENCED};
+ executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
memoryTypeList = {MemoryType::ASHMEM};
} break;
}
@@ -765,7 +827,7 @@
const TestModel& coupledModel) {
const std::vector<OutputType> outputTypesList = {OutputType::FULLY_SPECIFIED};
const std::vector<bool> measureTimingList = {false, true};
- const std::vector<Executor> executorList = {Executor::SYNC, Executor::FENCED};
+ const std::vector<Executor> executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
for (const OutputType outputType : outputTypesList) {
for (const bool measureTiming : measureTimingList) {
diff --git a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
index 1929750..596f8ae 100644
--- a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
@@ -203,6 +203,10 @@
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
}
+ ndk::ScopedAStatus configureExecutionBurst(std::shared_ptr<IBurst>*) override {
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+ }
};
template <typename... Args>
@@ -333,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}},
});
}
};
@@ -366,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}},
});
}
@@ -383,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}},
});
}
@@ -400,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}},
});
}
@@ -417,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}},
});
}
}
@@ -452,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}},
});
}
@@ -549,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}},
});
}
@@ -568,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}},
});
}
@@ -586,7 +590,7 @@
validateAllocate({
.dimensions = {1},
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 2, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 2, .probability = 1.0f}},
});
}
@@ -620,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);
@@ -866,6 +870,9 @@
case Executor::SYNC:
EXPECT_EQ(executeSync(preparedModel, request), expectedStatus);
break;
+ case Executor::BURST:
+ EXPECT_EQ(executeBurst(preparedModel, request), expectedStatus);
+ break;
case Executor::FENCED:
EXPECT_EQ(executeFenced(preparedModel, request), expectedStatus);
break;
@@ -916,6 +923,35 @@
return executionStatus;
}
+ ErrorStatus executeBurst(const std::shared_ptr<IPreparedModel>& preparedModel,
+ const Request& request) {
+ // create burst
+ std::shared_ptr<IBurst> burst;
+ auto ret = preparedModel->configureExecutionBurst(&burst);
+ EXPECT_TRUE(ret.isOk()) << ret.getDescription();
+ EXPECT_NE(nullptr, burst.get());
+ if (!ret.isOk() || burst.get() == nullptr) {
+ return ErrorStatus::GENERAL_FAILURE;
+ }
+
+ // use -1 for all memory identifier tokens
+ const std::vector<int64_t> slots(request.pools.size(), -1);
+
+ ExecutionResult executionResult;
+ ret = burst->executeSynchronously(request, slots, false, kNoDeadline,
+ kOmittedTimeoutDuration, &executionResult);
+
+ if (!ret.isOk()) {
+ EXPECT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+ return static_cast<ErrorStatus>(ret.getServiceSpecificError());
+ }
+ const ErrorStatus executionStatus = executionResult.outputSufficientSize
+ ? ErrorStatus::NONE
+ : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+ EXPECT_EQ(executionResult.timing, kNoTiming);
+ return executionStatus;
+ }
+
const Executor kExecutor = std::get<Executor>(GetParam());
};
@@ -1125,12 +1161,15 @@
utils::toSigned(kTestOperand.dimensions).value());
if (deviceBuffer.buffer == nullptr) return;
- RequestMemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
- RequestMemoryPool deviceMemory = createDeviceMemoryPool(deviceBuffer.token);
+ // Use an incompatible dimension and make sure the length matches with the bad dimension.
auto badDimensions = utils::toSigned(kTestOperand.dimensions).value();
badDimensions[0] = 2;
+ const uint32_t badTestOperandDataSize = kTestOperandDataSize * 2;
+
+ RequestMemoryPool sharedMemory = createSharedMemoryPool(badTestOperandDataSize);
+ RequestMemoryPool deviceMemory = createDeviceMemoryPool(deviceBuffer.token);
RequestArgument sharedMemoryArg = {
- .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize},
+ .location = {.poolIndex = 0, .offset = 0, .length = badTestOperandDataSize},
.dimensions = badDimensions};
RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
RequestArgument deviceMemoryArgWithBadDimensions = {.location = {.poolIndex = 1},
@@ -1156,7 +1195,7 @@
ErrorStatus::GENERAL_FAILURE);
}
-const auto kExecutorChoices = testing::Values(Executor::SYNC, Executor::FENCED);
+const auto kExecutorChoices = testing::Values(Executor::SYNC, Executor::BURST, Executor::FENCED);
std::string printMemoryDomainExecutionTest(
const testing::TestParamInfo<MemoryDomainExecutionTestParam>& info) {
diff --git a/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
index 58db98f..9ace1a9 100644
--- a/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
@@ -51,6 +51,10 @@
using Results = std::tuple<ErrorStatus, std::vector<OutputShape>, Timing>;
using MaybeResults = std::optional<Results>;
+using ExecutionFunction =
+ std::function<MaybeResults(const std::shared_ptr<IPreparedModel>& preparedModel,
+ const Request& request, int64_t deadline)>;
+
static int64_t makeDeadline(DeadlineBoundType deadlineBoundType) {
const auto getNanosecondsSinceEpoch = [](const auto& time) -> int64_t {
const auto timeSinceEpoch = time.time_since_epoch();
@@ -177,13 +181,53 @@
std::move(executionResult.outputShapes), executionResult.timing});
}
+static MaybeResults executeBurst(const std::shared_ptr<IPreparedModel>& preparedModel,
+ const Request& request, int64_t deadline) {
+ SCOPED_TRACE("burst");
+ const bool measure = false;
+
+ // create burst
+ std::shared_ptr<IBurst> burst;
+ auto ret = preparedModel->configureExecutionBurst(&burst);
+ EXPECT_TRUE(ret.isOk()) << ret.getDescription();
+ EXPECT_NE(nullptr, burst.get());
+ if (!ret.isOk() || burst.get() == nullptr) {
+ return std::nullopt;
+ }
+
+ // use -1 for all memory identifier tokens
+ const std::vector<int64_t> slots(request.pools.size(), -1);
+
+ // run execution
+ ExecutionResult executionResult;
+ ret = burst->executeSynchronously(request, slots, measure, deadline, kOmittedTimeoutDuration,
+ &executionResult);
+ EXPECT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
+ << ret.getDescription();
+ if (!ret.isOk()) {
+ if (ret.getExceptionCode() != EX_SERVICE_SPECIFIC) {
+ return std::nullopt;
+ }
+ return MaybeResults(
+ {static_cast<ErrorStatus>(ret.getServiceSpecificError()), {}, kNoTiming});
+ }
+
+ // return results
+ return MaybeResults({executionResult.outputSufficientSize
+ ? ErrorStatus::NONE
+ : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE,
+ std::move(executionResult.outputShapes), executionResult.timing});
+}
+
void runExecutionTest(const std::shared_ptr<IPreparedModel>& preparedModel,
const TestModel& testModel, const Request& request,
- const ExecutionContext& context, DeadlineBoundType deadlineBound) {
+ const ExecutionContext& context, bool synchronous,
+ DeadlineBoundType deadlineBound) {
+ const ExecutionFunction execute = synchronous ? executeSynchronously : executeBurst;
const auto deadline = makeDeadline(deadlineBound);
// Perform execution and unpack results.
- const auto results = executeSynchronously(preparedModel, request, deadline);
+ const auto results = execute(preparedModel, request, deadline);
if (!results.has_value()) return;
const auto& [status, outputShapes, timing] = results.value();
@@ -235,8 +279,11 @@
void runExecutionTests(const std::shared_ptr<IPreparedModel>& preparedModel,
const TestModel& testModel, const Request& request,
const ExecutionContext& context) {
- for (auto deadlineBound : deadlineBounds) {
- runExecutionTest(preparedModel, testModel, request, context, deadlineBound);
+ for (bool synchronous : {false, true}) {
+ for (auto deadlineBound : deadlineBounds) {
+ runExecutionTest(preparedModel, testModel, request, context, synchronous,
+ deadlineBound);
+ }
}
}
diff --git a/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp b/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp
index 3be4c1b..29e2471 100644
--- a/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp
@@ -16,7 +16,9 @@
#define LOG_TAG "neuralnetworks_aidl_hal_test"
+#include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h>
#include <android/binder_auto_utils.h>
+#include <variant>
#include <chrono>
@@ -77,6 +79,35 @@
ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()),
ErrorStatus::INVALID_ARGUMENT);
}
+
+ // burst
+ {
+ SCOPED_TRACE(message + " [burst]");
+
+ // create burst
+ std::shared_ptr<IBurst> burst;
+ auto ret = preparedModel->configureExecutionBurst(&burst);
+ ASSERT_TRUE(ret.isOk()) << ret.getDescription();
+ ASSERT_NE(nullptr, burst.get());
+
+ // use -1 for all memory identifier tokens
+ const std::vector<int64_t> slots(request.pools.size(), -1);
+
+ ExecutionResult executionResult;
+ const auto executeStatus = burst->executeSynchronously(
+ request, slots, measure, kNoDeadline, kOmittedTimeoutDuration, &executionResult);
+ ASSERT_FALSE(executeStatus.isOk());
+ ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+ ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()),
+ ErrorStatus::INVALID_ARGUMENT);
+ }
+}
+
+std::shared_ptr<IBurst> createBurst(const std::shared_ptr<IPreparedModel>& preparedModel) {
+ std::shared_ptr<IBurst> burst;
+ const auto ret = preparedModel->configureExecutionBurst(&burst);
+ if (!ret.isOk()) return nullptr;
+ return burst;
}
///////////////////////// REMOVE INPUT ////////////////////////////////////
@@ -110,6 +141,65 @@
removeOutputTest(preparedModel, request);
}
+void validateBurst(const std::shared_ptr<IPreparedModel>& preparedModel, const Request& request) {
+ // create burst
+ std::shared_ptr<IBurst> burst;
+ auto ret = preparedModel->configureExecutionBurst(&burst);
+ ASSERT_TRUE(ret.isOk()) << ret.getDescription();
+ ASSERT_NE(nullptr, burst.get());
+
+ const auto test = [&burst, &request](const std::vector<int64_t>& slots) {
+ ExecutionResult executionResult;
+ const auto executeStatus =
+ burst->executeSynchronously(request, slots, /*measure=*/false, kNoDeadline,
+ kOmittedTimeoutDuration, &executionResult);
+ ASSERT_FALSE(executeStatus.isOk());
+ ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+ ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()),
+ ErrorStatus::INVALID_ARGUMENT);
+ };
+
+ int64_t currentSlot = 0;
+ std::vector<int64_t> slots;
+ slots.reserve(request.pools.size());
+ for (const auto& pool : request.pools) {
+ if (pool.getTag() == RequestMemoryPool::Tag::pool) {
+ slots.push_back(currentSlot++);
+ } else {
+ slots.push_back(-1);
+ }
+ }
+
+ constexpr int64_t invalidSlot = -2;
+
+ // validate failure when invalid memory identifier token value
+ for (size_t i = 0; i < request.pools.size(); ++i) {
+ const int64_t oldSlotValue = slots[i];
+
+ slots[i] = invalidSlot;
+ test(slots);
+
+ slots[i] = oldSlotValue;
+ }
+
+ // validate failure when request.pools.size() != memoryIdentifierTokens.size()
+ if (request.pools.size() > 0) {
+ slots = std::vector<int64_t>(request.pools.size() - 1, -1);
+ test(slots);
+ }
+
+ // validate failure when request.pools.size() != memoryIdentifierTokens.size()
+ slots = std::vector<int64_t>(request.pools.size() + 1, -1);
+ test(slots);
+
+ // validate failure when invalid memory identifier token value
+ const auto freeStatus = burst->releaseMemoryResource(invalidSlot);
+ ASSERT_FALSE(freeStatus.isOk());
+ ASSERT_EQ(freeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+ ASSERT_EQ(static_cast<ErrorStatus>(freeStatus.getServiceSpecificError()),
+ ErrorStatus::INVALID_ARGUMENT);
+}
+
void validateRequestFailure(const std::shared_ptr<IPreparedModel>& preparedModel,
const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeSynchronously]");
diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
index 2d91b8e..0c3a196 100644
--- a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
@@ -127,6 +127,8 @@
// Forward declaration from ValidateRequest.cpp
void validateRequest(const std::shared_ptr<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateRequest.cpp
+void validateBurst(const std::shared_ptr<IPreparedModel>& preparedModel, const Request& request);
+// Forward declaration from ValidateRequest.cpp
void validateRequestFailure(const std::shared_ptr<IPreparedModel>& preparedModel,
const Request& request);
@@ -140,6 +142,7 @@
if (preparedModel == nullptr) return;
validateRequest(preparedModel, request);
+ validateBurst(preparedModel, request);
// HIDL also had test that expected executeFenced to fail on received null fd (-1). This is not
// allowed in AIDL and will result in EX_TRANSACTION_FAILED.
}
@@ -178,8 +181,6 @@
std::string toString(Executor executor) {
switch (executor) {
- case Executor::ASYNC:
- return "ASYNC";
case Executor::SYNC:
return "SYNC";
case Executor::BURST:
diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h
index 9b81ee1..4312d3a 100644
--- a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h
@@ -52,7 +52,7 @@
std::shared_ptr<IPreparedModel>* preparedModel,
bool reportSkipping = true);
-enum class Executor { ASYNC, SYNC, BURST, FENCED };
+enum class Executor { SYNC, BURST, FENCED };
std::string toString(Executor executor);
diff --git a/neuralnetworks/utils/README.md b/neuralnetworks/utils/README.md
index 45ca0b4..87b3f9f 100644
--- a/neuralnetworks/utils/README.md
+++ b/neuralnetworks/utils/README.md
@@ -49,7 +49,9 @@
(i.e., not as a nested class) or used in a subsequent version of the NN HAL. Prefer using `convert`
over `unvalidatedConvert`.
-# HIDL Interface Lifetimes across Processes
+# Interface Lifetimes across Processes
+
+## HIDL
Some notes about HIDL interface objects and lifetimes across processes:
@@ -68,7 +70,20 @@
If the process which created the HIDL interface object dies, any call on this object from another
process will result in a HIDL transport error with the code `DEAD_OBJECT`.
-# Protecting Asynchronous Calls across HIDL
+## AIDL
+
+We use NDK backend for AIDL interfaces. Handling of lifetimes is generally the same with the
+following differences:
+* Interfaces inherit from `ndk::ICInterface`, which inherits from `ndk::SharedRefBase`. The latter
+ is an analog of `::android::RefBase` using `std::shared_ptr` for reference counting.
+* AIDL calls return `ndk::ScopedAStatus` which wraps fields of types `binder_status_t` and
+ `binder_exception_t`. In case the call is made on a dead object, the call will return
+ `ndk::ScopedAStatus` with exception `EX_TRANSACTION_FAILED` and binder status
+ `STATUS_DEAD_OBJECT`.
+
+# Protecting Asynchronous Calls
+
+## Across HIDL
Some notes about asynchronous calls across HIDL:
@@ -95,3 +110,17 @@
driver process has died, and `DeathHandler` will unblock any thread waiting on the results of an
`IProtectedCallback` callback object that may otherwise not be signaled. In order for this to work,
the `IProtectedCallback` object must have been registered via `DeathHandler::protectCallback()`.
+
+## Across AIDL
+
+We use NDK backend for AIDL interfaces. Handling of asynchronous calls is generally the same with
+the following differences:
+* AIDL calls return `ndk::ScopedAStatus` which wraps fields of types `binder_status_t` and
+ `binder_exception_t`. In case the call is made on a dead object, the call will return
+ `ndk::ScopedAStatus` with exception `EX_TRANSACTION_FAILED` and binder status
+ `STATUS_DEAD_OBJECT`.
+* AIDL interface doesn't contain asynchronous `IPreparedModel::execute`.
+* Service death is handled using `AIBinder_DeathRecipient` object which is linked to an interface
+ object using `AIBinder_linkToDeath`. nnapi/hal/aidl/ProtectCallback.h provides `DeathHandler`
+ object that is a direct analog of HIDL `DeathHandler`, only using libbinder_ndk objects for
+ implementation.
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index 6162fe8..2ed1e40 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -35,8 +35,10 @@
"neuralnetworks_types",
],
shared_libs: [
+ "android.hardware.neuralnetworks-V1-ndk_platform",
"libhidlbase",
"libnativewindow",
+ "libbinder_ndk",
],
}
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 2f6112a..8fe6b90 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -32,6 +32,8 @@
// Shorthands
namespace aidl::android::hardware::neuralnetworks {
namespace aidl_hal = ::aidl::android::hardware::neuralnetworks;
+namespace hal = ::android::hardware::neuralnetworks;
+namespace nn = ::android::nn;
} // namespace aidl::android::hardware::neuralnetworks
// Shorthands
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
index c921885..05110bc 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
@@ -56,7 +56,7 @@
// Thread safe class
class DeathRecipient final : public hidl_death_recipient {
public:
- void serviceDied(uint64_t /*cookie*/, const wp<hidl::base::V1_0::IBase>& /*who*/) override;
+ void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who) override;
// Precondition: `killable` must be non-null.
void add(IProtectedCallback* killable) const;
// Precondition: `killable` must be non-null.
@@ -64,6 +64,7 @@
private:
mutable std::mutex mMutex;
+ mutable bool mIsDeadObject GUARDED_BY(mMutex) = false;
mutable std::vector<IProtectedCallback*> mObjects GUARDED_BY(mMutex);
};
@@ -78,14 +79,21 @@
~DeathHandler();
using Cleanup = std::function<void()>;
+ using Hold = base::ScopeGuard<Cleanup>;
+
// Precondition: `killable` must be non-null.
- [[nodiscard]] base::ScopeGuard<Cleanup> protectCallback(IProtectedCallback* killable) const;
+ // `killable` must outlive the return value `Hold`.
+ [[nodiscard]] Hold protectCallback(IProtectedCallback* killable) const;
+
+ // Precondition: `killable` must be non-null.
+ // `killable` must outlive the `DeathHandler`.
+ void protectCallbackForLifetimeOfDeathHandler(IProtectedCallback* killable) const;
private:
DeathHandler(sp<hidl::base::V1_0::IBase> object, sp<DeathRecipient> deathRecipient);
- sp<hidl::base::V1_0::IBase> kObject;
- sp<DeathRecipient> kDeathRecipient;
+ sp<hidl::base::V1_0::IBase> mObject;
+ sp<DeathRecipient> mDeathRecipient;
};
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ProtectCallback.cpp b/neuralnetworks/utils/common/src/ProtectCallback.cpp
index abe4cb6..18e1f3b 100644
--- a/neuralnetworks/utils/common/src/ProtectCallback.cpp
+++ b/neuralnetworks/utils/common/src/ProtectCallback.cpp
@@ -35,19 +35,25 @@
std::lock_guard guard(mMutex);
std::for_each(mObjects.begin(), mObjects.end(),
[](IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
+ mObjects.clear();
+ mIsDeadObject = true;
}
void DeathRecipient::add(IProtectedCallback* killable) const {
CHECK(killable != nullptr);
std::lock_guard guard(mMutex);
- mObjects.push_back(killable);
+ if (mIsDeadObject) {
+ killable->notifyAsDeadObject();
+ } else {
+ mObjects.push_back(killable);
+ }
}
void DeathRecipient::remove(IProtectedCallback* killable) const {
CHECK(killable != nullptr);
std::lock_guard guard(mMutex);
- const auto removedIter = std::remove(mObjects.begin(), mObjects.end(), killable);
- mObjects.erase(removedIter);
+ const auto newEnd = std::remove(mObjects.begin(), mObjects.end(), killable);
+ mObjects.erase(newEnd, mObjects.end());
}
nn::GeneralResult<DeathHandler> DeathHandler::create(sp<hidl::base::V1_0::IBase> object) {
@@ -67,19 +73,16 @@
}
DeathHandler::DeathHandler(sp<hidl::base::V1_0::IBase> object, sp<DeathRecipient> deathRecipient)
- : kObject(std::move(object)), kDeathRecipient(std::move(deathRecipient)) {
- CHECK(kObject != nullptr);
- CHECK(kDeathRecipient != nullptr);
+ : mObject(std::move(object)), mDeathRecipient(std::move(deathRecipient)) {
+ CHECK(mObject != nullptr);
+ CHECK(mDeathRecipient != nullptr);
}
DeathHandler::~DeathHandler() {
- if (kObject != nullptr && kDeathRecipient != nullptr) {
- const auto ret = kObject->unlinkToDeath(kDeathRecipient);
- const auto maybeSuccess = handleTransportError(ret);
- if (!maybeSuccess.has_value()) {
- LOG(ERROR) << maybeSuccess.error().message;
- } else if (!maybeSuccess.value()) {
- LOG(ERROR) << "IBase::linkToDeath returned false";
+ if (mObject != nullptr && mDeathRecipient != nullptr) {
+ const auto successful = mObject->unlinkToDeath(mDeathRecipient).isOk();
+ if (!successful) {
+ LOG(ERROR) << "IBase::linkToDeath failed";
}
}
}
@@ -87,9 +90,14 @@
[[nodiscard]] base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
IProtectedCallback* killable) const {
CHECK(killable != nullptr);
- kDeathRecipient->add(killable);
+ mDeathRecipient->add(killable);
return base::make_scope_guard(
- [deathRecipient = kDeathRecipient, killable] { deathRecipient->remove(killable); });
+ [deathRecipient = mDeathRecipient, killable] { deathRecipient->remove(killable); });
+}
+
+void DeathHandler::protectCallbackForLifetimeOfDeathHandler(IProtectedCallback* killable) const {
+ CHECK(killable != nullptr);
+ mDeathRecipient->add(killable);
}
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp
index 9f8b9bb..5f36dff 100644
--- a/neuralnetworks/utils/service/Android.bp
+++ b/neuralnetworks/utils/service/Android.bp
@@ -35,12 +35,15 @@
"neuralnetworks_utils_hal_1_1",
"neuralnetworks_utils_hal_1_2",
"neuralnetworks_utils_hal_1_3",
+ "neuralnetworks_utils_hal_aidl",
"neuralnetworks_utils_hal_common",
],
shared_libs: [
+ "android.hardware.neuralnetworks-V1-ndk_platform",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
"android.hardware.neuralnetworks@1.3",
+ "libbinder_ndk",
],
}
diff --git a/neuralnetworks/utils/service/src/Service.cpp b/neuralnetworks/utils/service/src/Service.cpp
index a59549d..c83bcc9 100644
--- a/neuralnetworks/utils/service/src/Service.cpp
+++ b/neuralnetworks/utils/service/src/Service.cpp
@@ -16,7 +16,9 @@
#include "Service.h"
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
#include <android-base/logging.h>
+#include <android/binder_manager.h>
#include <android/hardware/neuralnetworks/1.0/IDevice.h>
#include <android/hardware/neuralnetworks/1.1/IDevice.h>
#include <android/hardware/neuralnetworks/1.2/IDevice.h>
@@ -31,6 +33,7 @@
#include <nnapi/hal/1.1/Service.h>
#include <nnapi/hal/1.2/Service.h>
#include <nnapi/hal/1.3/Service.h>
+#include <nnapi/hal/aidl/Service.h>
#include <functional>
#include <memory>
@@ -42,11 +45,12 @@
namespace android::hardware::neuralnetworks::service {
namespace {
+namespace aidl_hal = ::aidl::android::hardware::neuralnetworks;
using getDeviceFn = std::add_pointer_t<nn::GeneralResult<nn::SharedDevice>(const std::string&)>;
-void getDevicesForVersion(const std::string& descriptor, getDeviceFn getDevice,
- std::vector<nn::SharedDevice>* devices,
- std::unordered_set<std::string>* registeredDevices) {
+void getHidlDevicesForVersion(const std::string& descriptor, getDeviceFn getDevice,
+ std::vector<nn::SharedDevice>* devices,
+ std::unordered_set<std::string>* registeredDevices) {
CHECK(devices != nullptr);
CHECK(registeredDevices != nullptr);
@@ -66,18 +70,52 @@
}
}
+void getAidlDevices(std::vector<nn::SharedDevice>* devices,
+ std::unordered_set<std::string>* registeredDevices) {
+ CHECK(devices != nullptr);
+ CHECK(registeredDevices != nullptr);
+
+ std::vector<std::string> names;
+ constexpr auto callback = [](const char* serviceName, void* names) {
+ static_cast<std::vector<std::string>*>(names)->emplace_back(serviceName);
+ };
+
+ // Devices with SDK level lower than 31 (Android S) don't have any AIDL drivers available, so
+ // there is no need for a workaround supported on lower levels.
+ if (__builtin_available(android __ANDROID_API_S__, *)) {
+ AServiceManager_forEachDeclaredInstance(aidl_hal::IDevice::descriptor,
+ static_cast<void*>(&names), callback);
+ }
+
+ for (const auto& name : names) {
+ if (const auto [it, unregistered] = registeredDevices->insert(name); unregistered) {
+ auto maybeDevice = aidl_hal::utils::getDevice(name);
+ if (maybeDevice.has_value()) {
+ auto device = std::move(maybeDevice).value();
+ CHECK(device != nullptr);
+ devices->push_back(std::move(device));
+ } else {
+ LOG(ERROR) << "getDevice(" << name << ") failed with " << maybeDevice.error().code
+ << ": " << maybeDevice.error().message;
+ }
+ }
+ }
+}
+
std::vector<nn::SharedDevice> getDevices() {
std::vector<nn::SharedDevice> devices;
std::unordered_set<std::string> registeredDevices;
- getDevicesForVersion(V1_3::IDevice::descriptor, &V1_3::utils::getDevice, &devices,
- ®isteredDevices);
- getDevicesForVersion(V1_2::IDevice::descriptor, &V1_2::utils::getDevice, &devices,
- ®isteredDevices);
- getDevicesForVersion(V1_1::IDevice::descriptor, &V1_1::utils::getDevice, &devices,
- ®isteredDevices);
- getDevicesForVersion(V1_0::IDevice::descriptor, &V1_0::utils::getDevice, &devices,
- ®isteredDevices);
+ getAidlDevices(&devices, ®isteredDevices);
+
+ getHidlDevicesForVersion(V1_3::IDevice::descriptor, &V1_3::utils::getDevice, &devices,
+ ®isteredDevices);
+ getHidlDevicesForVersion(V1_2::IDevice::descriptor, &V1_2::utils::getDevice, &devices,
+ ®isteredDevices);
+ getHidlDevicesForVersion(V1_1::IDevice::descriptor, &V1_1::utils::getDevice, &devices,
+ ®isteredDevices);
+ getHidlDevicesForVersion(V1_0::IDevice::descriptor, &V1_0::utils::getDevice, &devices,
+ ®isteredDevices);
return devices;
}
diff --git a/power/1.0/default/Power.cpp b/power/1.0/default/Power.cpp
index 51f87f5..b91a6e8 100644
--- a/power/1.0/default/Power.cpp
+++ b/power/1.0/default/Power.cpp
@@ -35,7 +35,6 @@
}
Power::~Power() {
- delete(mModule);
}
// Methods from ::android::hardware::power::V1_0::IPower follow.
diff --git a/power/stats/aidl/Android.bp b/power/stats/aidl/Android.bp
index f4955e2..0dbf9b4 100644
--- a/power/stats/aidl/Android.bp
+++ b/power/stats/aidl/Android.bp
@@ -38,7 +38,8 @@
},
},
cpp: {
- enabled: false,
+ enabled: true,
},
},
+ host_supported: true,
}
diff --git a/power/stats/aidl/android/hardware/power/stats/IPowerStats.aidl b/power/stats/aidl/android/hardware/power/stats/IPowerStats.aidl
index 7a95f74..edc43ea 100644
--- a/power/stats/aidl/android/hardware/power/stats/IPowerStats.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/IPowerStats.aidl
@@ -32,7 +32,7 @@
* A PowerEntity is defined as a platform subsystem, peripheral, or power domain that impacts
* the total device power consumption.
*
- * @return List of information on each PowerEntity
+ * @return List of information on each PowerEntity for which state residency can be requested.
*/
PowerEntity[] getPowerEntityInfo();
@@ -52,11 +52,12 @@
* Passing an empty list will return state residency for all available PowerEntitys.
* ID of each PowerEntity is contained in PowerEntityInfo.
*
- * @return StateResidency since boot for each requested PowerEntity
+ * @return StateResidencyResults since boot for each requested and available PowerEntity. Note
+ * that StateResidencyResult for a given PowerEntity may not always be available. Clients shall
+ * not rely on StateResidencyResult always being returned for every request.
*
- * Returns the following service-specific exceptions in order of highest priority:
- * - STATUS_BAD_VALUE if an invalid powerEntityId is provided
- * - STATUS_FAILED_TRANSACTION if any StateResidencyResult fails to be returned
+ * Returns the following exception codes:
+ * - EX_ILLEGAL_ARGUMENT if an invalid powerEntityId is provided
*/
StateResidencyResult[] getStateResidency(in int[] powerEntityIds);
@@ -66,7 +67,7 @@
* An EnergyConsumer is a device subsystem or peripheral that consumes energy. Energy
* consumption data may be used by framework for the purpose of power attribution.
*
- * @return List of EnergyConsumers that are available.
+ * @return List of EnergyConsumers for which energy consumption can be requested.
*/
EnergyConsumer[] getEnergyConsumerInfo();
@@ -74,38 +75,40 @@
* Reports the energy consumed since boot by each requested EnergyConsumer.
*
* @param energyConsumerIds List of IDs of EnergyConsumers for which data is requested.
- * Passing an empty list will return state residency for all available EnergyConsumers.
+ * Passing an empty list will return results for all available EnergyConsumers.
*
- * @return Energy consumed since boot for each requested EnergyConsumer
+ * @return Energy consumed since boot for each requested and available EnergyConsumer. Note
+ * that EnergyConsumerResult for a given EnergyConsumer may not always be available. Clients
+ * shall not rely on EnergyConsumerResult always being returned for every request.
*
- * Returns the following service-specific exceptions in order of highest priority:
- * - STATUS_BAD_VALUE if an invalid energyConsumerId is provided
- * - STATUS_FAILED_TRANSACTION if any EnergyConsumerResult fails to be returned
+ * Returns the following exception codes:
+ * - EX_ILLEGAL_ARGUMENT if an invalid energyConsumerId is provided
*/
EnergyConsumerResult[] getEnergyConsumed(in int[] energyConsumerIds);
/**
- * Return information related to all channels monitored by Energy Meters.
+ * Return information related to all Channels monitored by Energy Meters.
*
* An Energy Meter is a device that monitors energy and may support monitoring multiple
* channels simultaneously. A channel may correspond a bus, sense resistor, or power rail.
*
- * @return Channels monitored by Energy Meters.
+ * @return All Channels for which energy measurements can be requested.
*/
Channel[] getEnergyMeterInfo();
/**
- * Reports accumulated energy for each specified channel.
+ * Reports accumulated energy for each specified Channel.
*
* @param channelIds IDs of channels for which data is requested.
* Passing an empty list will return energy measurements for all available channels.
* ID of each channel is contained in ChannelInfo.
*
- * @return Energy measured since boot for each requested channel
+ * @return Energy measured since boot for each requested and available Channel. Note
+ * that EnergyMeasurement for a given Channel may not always be available. Clients
+ * shall not rely on EnergyMeasurement always being returned for every request.
*
- * Returns the following service-specific exceptions in order of highest priority:
- * - STATUS_BAD_VALUE if an invalid channelId is provided
- * - STATUS_FAILED_TRANSACTION if any EnergyMeasurement fails to be returned
+ * Returns the following exception codes:
+ * - EX_ILLEGAL_ARGUMENT if an invalid channelId is provided
*/
EnergyMeasurement[] readEnergyMeter(in int[] channelIds);
}
diff --git a/power/stats/aidl/default/FakeEnergyMeter.h b/power/stats/aidl/default/FakeEnergyMeter.h
index f0d4ee7..56dcdcc 100644
--- a/power/stats/aidl/default/FakeEnergyMeter.h
+++ b/power/stats/aidl/default/FakeEnergyMeter.h
@@ -60,9 +60,12 @@
*_aidl_return = mEnergyMeasurements;
} else {
for (int32_t id : in_channelIds) {
- if (id >= 0 && id < mEnergyMeasurements.size()) {
- _aidl_return->push_back(mEnergyMeasurements[id]);
+ // check for invalid ids
+ if (id < 0 || id >= mEnergyMeasurements.size()) {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
}
+
+ _aidl_return->push_back(mEnergyMeasurements[id]);
}
}
diff --git a/power/stats/aidl/default/PowerStats.cpp b/power/stats/aidl/default/PowerStats.cpp
index 1373502..7cf591e 100644
--- a/power/stats/aidl/default/PowerStats.cpp
+++ b/power/stats/aidl/default/PowerStats.cpp
@@ -81,14 +81,12 @@
return getStateResidency(v, _aidl_return);
}
- binder_status_t err = STATUS_OK;
-
std::unordered_map<std::string, std::vector<StateResidency>> stateResidencies;
for (const int32_t id : in_powerEntityIds) {
- // skip any invalid ids
+ // check for invalid ids
if (id < 0 || id >= mPowerEntityInfos.size()) {
- continue;
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
}
// Check to see if we already have data for the given id
@@ -106,12 +104,12 @@
};
_aidl_return->emplace_back(res);
} else {
- // Failed to retrieve results for the given id.
- err = STATUS_FAILED_TRANSACTION;
+ // Failed to get results for the given id.
+ LOG(ERROR) << "Failed to get results for " << powerEntityName;
}
}
- return ndk::ScopedAStatus::fromStatus(err);
+ return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerStats::getEnergyConsumerInfo(std::vector<EnergyConsumer>* _aidl_return) {
@@ -132,12 +130,10 @@
return getEnergyConsumed(v, _aidl_return);
}
- binder_status_t err = STATUS_OK;
-
for (const auto id : in_energyConsumerIds) {
- // skip any invalid ids
+ // check for invalid ids
if (id < 0 || id >= mEnergyConsumers.size()) {
- continue;
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
}
auto optionalResult = mEnergyConsumers[id]->getEnergyConsumed();
@@ -146,12 +142,12 @@
result.id = id;
_aidl_return->emplace_back(result);
} else {
- // Failed to retrieve results for the given id.
- err = STATUS_FAILED_TRANSACTION;
+ // Failed to get results for the given id.
+ LOG(ERROR) << "Failed to get results for " << mEnergyConsumerInfos[id].name;
}
}
- return ndk::ScopedAStatus::fromStatus(err);
+ return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerStats::getEnergyMeterInfo(std::vector<Channel>* _aidl_return) {
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 56ce809..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.
@@ -230,6 +231,7 @@
* RadioError:NONE
* RadioError:RADIO_NOT_AVAILABLE
* RadioError:INTERNAL_ERR
+ * RadioError:REQUEST_NOT_SUPPORTED
*/
oneway setNrDualConnectivityStateResponse(RadioResponseInfo info);
@@ -242,6 +244,7 @@
* RadioError:NONE
* RadioError:RADIO_NOT_AVAILABLE
* RadioError:INTERNAL_ERR
+ * RadioError:REQUEST_NOT_SUPPORTED
*/
oneway isNrDualConnectivityEnabledResponse(RadioResponseInfo info, bool isEnabled);
@@ -340,6 +343,7 @@
* RadioError:RADIO_NOT_AVAILABLE
* RadioError:MODEM_ERR
* RadioError:INVALID_ARGUMENTS
+ * RadioError:REQUEST_NOT_SUPPORTED
*/
oneway setDataThrottlingResponse(RadioResponseInfo info);
@@ -430,7 +434,61 @@
* RadioError:RADIO_NOT_AVAILABLE
* RadioError:INTERNAL_ERR
* RadioError:MODEM_ERR
+ * RadioError:REQUEST_NOT_SUPPORTED
*/
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 6400c63..3fd31cc 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -813,6 +813,11 @@
* see: 3GPP TS 24.501 Section 9.11.2.8.
*/
int32_t mappedHplmnSD;
+
+ /**
+ * Field to indicate the current status of the slice.
+ */
+ SliceStatus status;
};
/**
@@ -986,9 +991,9 @@
*/
vec<UrspRule> urspRules;
/**
- * Struct containing all NSSAIs (list of slice info).
+ * List of all slices.
*/
- Nssais nssais;
+ vec<SliceInfo> sliceInfo;
};
/**
@@ -1011,7 +1016,6 @@
vec<RouteSelectionDescriptor> routeSelectionDescriptor;
};
-
/**
* This struct represents a single route selection descriptor as defined in
* 3GPP TS 24.526.
@@ -1023,18 +1027,6 @@
*/
uint8_t precedence;
/**
- * Parameters defining this RouteSelectionDescriptor. The length of the vector
- * must be >= 1.
- */
- vec<RouteSelectionDescriptorParams> routeSelectionDescriptorParams;
-};
-
-/**
- * This struct represents a route selection descriptor. A valid struct must have
- * at least one of the vectors non-empty.
- */
-struct RouteSelectionDescriptorParams {
- /**
* Valid values are IP, IPV6 and IPV4V6.
*/
OptionalPdpProtocolType sessionType;
@@ -1067,47 +1059,13 @@
SscMode value;
};
-/**
- * This struct contains all NSSAIs (lists of slices).
- */
-struct Nssais {
- /**
- * These are all the slices configured by the network. This includes allowed
- * and rejected slices, as well as slices that are neither allowed nor rejected
- * yet. Empty vector indicates that no slices are configured, and in that case
- * allowed and rejected vectors must be empty as well.
- */
- vec<SliceInfo> configured;
- /**
- * These are all the slices that the UE is allowed to use. All these slices
- * must be configured as well. Empty vector indicates that no slices are
- * allowed yet.
- */
- vec<SliceInfo> allowed;
- /**
- * These are all the slices that the UE is not allowed to use. All these slices
- * must be configured as well. Empty vector indicates that no slices are
- * rejected yet.
- */
- vec<RejectedSliceInfo> rejected;
- /**
- * Default configured NSSAI
- */
- vec<SliceInfo> defaultConfigured;
-};
-
-/**
- * This struct represents a network slice rejected by the network. It contains a
- * rejectionCause corresponding to a rejected network slice.
- */
-struct RejectedSliceInfo {
- SliceInfo sliceInfo;
- SliceRejectionCause rejectionCause;
-};
-
-enum SliceRejectionCause : int32_t {
- NOT_AVAILABLE_IN_PLMN,
- NOT_AVAILABLE_IN_REG_AREA,
+enum SliceStatus : int32_t {
+ UNKNOWN,
+ CONFIGURED, // Configured but not allowed or rejected yet
+ ALLOWED, // Allowed to be used
+ REJECTED_NOT_AVAILABLE_IN_PLMN, // Rejected because not available in PLMN
+ REJECTED_NOT_AVAILABLE_IN_REG_AREA, // Rejected because not available in reg area
+ DEFAULT_CONFIGURED, // Considered valid when configured/allowed slices are not available
};
/**
@@ -1136,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 e82c01a..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);
}
@@ -172,7 +175,13 @@
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 (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+ } else {
+ EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+ }
}
/*
@@ -369,10 +378,17 @@
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);
- ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+ if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+ } else {
+ ASSERT_TRUE(
+ CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
::android::hardware::radio::V1_6::RadioError::NONE}));
+ }
}
/*
@@ -387,10 +403,17 @@
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);
- ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+ if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+ } else {
+ ASSERT_TRUE(
+ CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
::android::hardware::radio::V1_6::RadioError::NONE}));
+ }
}
/*
@@ -406,13 +429,18 @@
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);
- ASSERT_TRUE(
- CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
- {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
- ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
- ::android::hardware::radio::V1_6::RadioError::NONE,
- ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
-
+ if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+ } else {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+ ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+ ::android::hardware::radio::V1_6::RadioError::NONE,
+ ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+ }
serial = GetRandomSerialNumber();
res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER,
@@ -421,13 +449,18 @@
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);
- ASSERT_TRUE(
- CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
- {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
- ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
- ::android::hardware::radio::V1_6::RadioError::NONE,
- ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
-
+ if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+ } else {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+ ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+ ::android::hardware::radio::V1_6::RadioError::NONE,
+ ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+ }
serial = GetRandomSerialNumber();
res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::HOLD, 60000);
@@ -436,13 +469,18 @@
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);
- ASSERT_TRUE(
- CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
- {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
- ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
- ::android::hardware::radio::V1_6::RadioError::NONE,
- ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
-
+ if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+ } else {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+ ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+ ::android::hardware::radio::V1_6::RadioError::NONE,
+ ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+ }
serial = GetRandomSerialNumber();
res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60000);
@@ -450,12 +488,18 @@
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);
- ASSERT_TRUE(
- CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
- {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
- ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
- ::android::hardware::radio::V1_6::RadioError::NONE,
- ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+ if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+ } else {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo.error,
+ {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+ ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+ ::android::hardware::radio::V1_6::RadioError::NONE,
+ ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+ }
}
/*
@@ -692,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(
@@ -702,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 8034fd2..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();
}
@@ -849,7 +853,9 @@
/* 1.4 Apis */
Return<void> RadioResponse_v1_6::emergencyDialResponse(
- 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();
}
@@ -1230,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/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
index 8915970..8667f0a 100644
--- a/radio/config/1.3/types.hal
+++ b/radio/config/1.3/types.hal
@@ -28,6 +28,14 @@
* or android.hardware.radio@1.6::LinkCapacityEstimate:secondaryUplinkCapacityKbps
* when given from android.hardware.radio@1.6::RadioIndication:currentLinkCapacityEstimate
* </li>
+ * <li> calling android.hardware.radio@1.6::IRadio.setNrDualConnectivityState
+ * or querying android.hardware.radio@1.6::IRadio.isNrDualConnectivityEnabled
+ * </li>
+ * <li>Requesting android.hardware.radio@1.6::IRadio.setDataThrottling()
+ * </li>
+ * <li>Providing android.hardware.radio@1.6::SlicingConfig through
+ * android.hardware.radio@1.6::getSlicingConfig()
+ * </li>
* </ul>
*/
bool modemReducedFeatureSet1;
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
index 895ae08..50038eb 100644
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
@@ -37,7 +37,7 @@
#include "vts_test_util.h"
-using namespace ::android::hardware::radio::config::V1_2;
+using namespace ::android::hardware::radio::config::V1_3;
using ::android::sp;
using ::android::hardware::hidl_string;
@@ -46,9 +46,11 @@
using ::android::hardware::Void;
using ::android::hardware::radio::config::V1_1::ModemsConfig;
using ::android::hardware::radio::config::V1_1::PhoneCapability;
+using ::android::hardware::radio::config::V1_2::IRadioConfigIndication;
using ::android::hardware::radio::config::V1_2::SimSlotStatus;
using ::android::hardware::radio::config::V1_3::HalDeviceCapabilities;
using ::android::hardware::radio::config::V1_3::IRadioConfig;
+using ::android::hardware::radio::config::V1_3::IRadioConfigResponse;
using ::android::hardware::radio::V1_0::RadioResponseInfo;
#define RADIO_SERVICE_NAME "slot1"
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
index 11e3cce..5501ae2 100644
--- a/radio/config/1.3/vts/functional/radio_config_response.cpp
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -64,8 +64,9 @@
}
Return<void> RadioConfigResponse::getHalDeviceCapabilitiesResponse(
- const ::android::hardware::radio::V1_6::RadioResponseInfo& /* info */,
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
const ::android::hardware::radio::config::V1_3::HalDeviceCapabilities& capabilities) {
halDeviceCapabilities = capabilities;
+ parent.notify(info.serial);
return Void();
}
diff --git a/scripts/list_hal_vts.py b/scripts/list_hal_vts.py
new file mode 100755
index 0000000..1fb51a5
--- /dev/null
+++ b/scripts/list_hal_vts.py
@@ -0,0 +1,143 @@
+#!/usr/bin/python3
+
+"""
+List VTS tests for each HAL by parsing module-info.json.
+
+Example usage:
+
+ # First, build modules-info.json
+ m -j "${ANDROID_PRODUCT_OUT#$ANDROID_BUILD_TOP/}/module-info.json"
+
+ # List with pretty-printed JSON. *IDL packages without a VTS module will show up
+ # as keys with empty lists.
+ ./list_hals_vts.py | python3 -m json.tool
+
+ # List with CSV. *IDL packages without a VTS module will show up as a line with
+ # empty value in the VTS module column.
+ ./list_hals_vts.py --csv
+"""
+
+import argparse
+import collections
+import csv
+import io
+import json
+import os
+import logging
+import pathlib
+import re
+import sys
+
+PATH_PACKAGE_PATTERN = re.compile(
+ r'^hardware/interfaces/(?P<path>(?:\w+/)*?)(?:aidl|(?P<version>\d+\.\d+))/.*')
+
+
+class CriticalHandler(logging.StreamHandler):
+ def emit(self, record):
+ super(CriticalHandler, self).emit(record)
+ if record.levelno >= logging.CRITICAL:
+ sys.exit(1)
+
+
+logger = logging.getLogger(__name__)
+logger.addHandler(CriticalHandler())
+
+
+def default_json():
+ out = os.environ.get('ANDROID_PRODUCT_OUT')
+ if not out: return None
+ return os.path.join(out, 'module-info.json')
+
+
+def infer_package(path):
+ """
+ Infer package from a relative path from build top where a VTS module lives.
+
+ :param path: a path like 'hardware/interfaces/vibrator/aidl/vts'
+ :return: The inferred *IDL package, e.g. 'android.hardware.vibrator'
+
+ >>> infer_package('hardware/interfaces/automotive/sv/1.0/vts/functional')
+ 'android.hardware.automotive.sv@1.0'
+ >>> infer_package('hardware/interfaces/vibrator/aidl/vts')
+ 'android.hardware.vibrator'
+ """
+ mo = re.match(PATH_PACKAGE_PATTERN, path)
+ if not mo: return None
+ package = 'android.hardware.' + ('.'.join(pathlib.Path(mo.group('path')).parts))
+ if mo.group('version'):
+ package += '@' + mo.group('version')
+ return package
+
+
+def load_modules_info(json_file):
+ """
+ :param json_file: The path to modules-info.json
+ :return: a dictionary, where the keys are inferred *IDL package names, and
+ values are a list of VTS modules with that inferred package name.
+ """
+ with open(json_file) as fp:
+ root = json.load(fp)
+ ret = collections.defaultdict(list)
+ for module_name, module_info in root.items():
+ if 'vts' not in module_info.get('compatibility_suites', []):
+ continue
+ for path in module_info.get('path', []):
+ inferred_package = infer_package(path)
+ if not inferred_package:
+ continue
+ ret[inferred_package].append(module_name)
+ return ret
+
+
+def add_missing_idl(vts_modules):
+ top = os.environ.get("ANDROID_BUILD_TOP")
+ interfaces = None
+ if top:
+ interfaces = os.path.join(top, "hardware", "interfaces")
+ else:
+ logger.warning("Missing ANDROID_BUILD_TOP")
+ interfaces = "hardware/interfaces"
+ if not os.path.isdir(interfaces):
+ logger.error("Not adding missing *IDL modules because missing hardware/interfaces dir")
+ return
+ assert not interfaces.endswith(os.path.sep)
+ for root, dirs, files in os.walk(interfaces):
+ for dir in dirs:
+ full_dir = os.path.join(root, dir)
+ assert full_dir.startswith(interfaces)
+ top_rel_dir = os.path.join('hardware', 'interfaces', full_dir[len(interfaces) + 1:])
+ inferred_package = infer_package(top_rel_dir)
+ if inferred_package is None:
+ continue
+ if inferred_package not in vts_modules:
+ vts_modules[inferred_package] = []
+
+
+def main():
+ parser = argparse.ArgumentParser(__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument('json', metavar='module-info.json', default=default_json(), nargs='?')
+ parser.add_argument('--csv', action='store_true', help='Print CSV. If not specified print JSON.')
+ args = parser.parse_args()
+ if not args.json:
+ logger.critical('No module-info.json is specified or found.')
+ vts_modules = load_modules_info(args.json)
+ add_missing_idl(vts_modules)
+
+ if args.csv:
+ out = io.StringIO()
+ writer = csv.writer(out, )
+ writer.writerow(["package", "vts_module"])
+ for package, modules in vts_modules.items():
+ if not modules:
+ writer.writerow([package, ""])
+ for module in modules:
+ writer.writerow([package, module])
+ result = out.getvalue()
+ else:
+ result = json.dumps(vts_modules)
+
+ print(result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 54cb4b8..6766d99 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -19,7 +19,8 @@
stability: "vintf",
backend: {
java: {
- sdk_version: "module_current",
+ platform_apis: true,
+ srcs_available: true,
},
ndk: {
vndk: {
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/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
index 29ff8f8..6da124f 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum Algorithm {
RSA = 1,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl
index 893b016..90f2e6e 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
parcelable AttestationKey {
byte[] keyBlob;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
index 4421619..c952a31 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@VintfStability
parcelable BeginResult {
long challenge;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
index e9652c3..0049883 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum BlockMode {
ECB = 1,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
index 5d1cc68..645f0a7 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@VintfStability
parcelable Certificate {
byte[] encodedCertificate;
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
similarity index 88%
copy from biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
copy to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
index c5288fd..d04d49c 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -31,10 +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.common;
+package android.hardware.security.keymint;
+/* @hide */
@VintfStability
-parcelable HardwareInfo {
- String deviceName;
- String hardwareVersion;
- String serialNumber;
+parcelable DeviceInfo {
+ byte[] deviceInfo;
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
index 5055d75..0df7096 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum Digest {
NONE = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
index 1a7e9b5..6b4a9ae 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum EcCurve {
P_224 = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
index 2eb6e35..69ec4ce 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum ErrorCode {
OK = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
index 30fe6dc..2e07924 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
parcelable HardwareAuthToken {
long challenge;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
index ae64110..dfc98f0 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum HardwareAuthenticatorType {
NONE = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
index 7150c44..4f6fb28 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@VintfStability
interface IKeyMintDevice {
android.hardware.security.keymint.KeyMintHardwareInfo getHardwareInfo();
@@ -45,6 +47,7 @@
android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose purpose, in byte[] keyBlob, in android.hardware.security.keymint.KeyParameter[] params, in android.hardware.security.keymint.HardwareAuthToken authToken);
void deviceLocked(in boolean passwordOnly, in @nullable android.hardware.security.secureclock.TimeStampToken timestampToken);
void earlyBootEnded();
+ byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
byte[] performOperation(in byte[] request);
const int AUTH_TOKEN_MAC_LENGTH = 32;
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
index 80ed526..5ac2b4a 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@VintfStability
interface IKeyMintOperation {
void updateAad(in byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken timeStampToken);
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 a864c3c..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
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,10 +32,11 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@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/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
index 994bd4c..008381f 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@VintfStability
parcelable KeyCharacteristics {
android.hardware.security.keymint.SecurityLevel securityLevel = android.hardware.security.keymint.SecurityLevel.SOFTWARE;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCreationResult.aidl
index 4139436..9f77d3e 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@VintfStability
parcelable KeyCreationResult {
byte[] keyBlob;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
index 1ad7c51..9560d8d 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum KeyFormat {
X509 = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
index 7747c59..2113e42a 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
parcelable KeyMintHardwareInfo {
int versionNumber;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
index acaf60d..4b3c659 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum KeyOrigin {
GENERATED = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
index 21b083c..c5a1e01 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
parcelable KeyParameter {
android.hardware.security.keymint.Tag tag = android.hardware.security.keymint.Tag.INVALID;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterValue.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterValue.aidl
index c79614a..7a0b074 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterValue.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterValue.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
union KeyParameterValue {
int invalid;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
index 61bb7e4..b84bec1 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum KeyPurpose {
ENCRYPT = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
index b4caeed..8095e8c 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@VintfStability
parcelable MacedPublicKey {
byte[] macedKey;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
index 96b63e1..dba4a8a 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum PaddingMode {
NONE = 1,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
index 46f602f..d1610b4 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@VintfStability
parcelable ProtectedData {
byte[] protectedData;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
index c720d6d..0d278e0 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum SecurityLevel {
SOFTWARE = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
index 2469d27..7591318 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum Tag {
INVALID = 0,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
index 75a19a3..a7d1de5 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.keymint;
+/* @hide */
@Backing(type="int") @VintfStability
enum TagType {
INVALID = 0,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
index 8300b0d..1820893 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
@@ -18,6 +18,7 @@
/**
* Algorithms provided by IKeyMintDevice implementations.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
index 8167ceb..b4bc60c 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
@@ -22,6 +22,7 @@
* Contains a key blob with Tag::ATTEST_KEY that can be used to sign an attestation certificate,
* and the DER-encoded X.501 Subject Name that will be placed in the Issuer field of the attestation
* certificate.
+ * @hide
*/
@VintfStability
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
index aaf9f3c..2304a58 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
@@ -21,6 +21,7 @@
/**
* This is all the results returned by the IKeyMintDevice begin() function.
+ * @hide
*/
@VintfStability
parcelable BeginResult {
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
index 629c89f..749da81 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
@@ -18,6 +18,7 @@
/**
* Symmetric block cipher modes provided by IKeyMintDevice implementations.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
index 0e5d898..21dfdd5 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
@@ -18,6 +18,7 @@
/**
* This encodes an IKeyMintDevice certificate, generated for a KeyMint asymmetric public key.
+ * @hide
*/
@VintfStability
parcelable Certificate {
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/Digest.aidl b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
index b44da5a..a8768c3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
@@ -18,6 +18,7 @@
/**
* Digests provided by keyMint implementations.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
index b9d1646..5b1c10c 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
@@ -18,6 +18,7 @@
/**
* Supported EC curves, used in ECDSA
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
index 95b38f2..0e2c5f2 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
@@ -19,6 +19,7 @@
/**
* KeyMint error codes. Aidl will return these error codes as service specific
* errors in EX_SERVICE_SPECIFIC.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
index 57150d5..0933bd5 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -27,6 +27,7 @@
* passed to begin(), update(), and finish() to prove that authentication occurred. See those
* methods for more details. It is up to the caller to determine which of the generated auth tokens
* is appropriate for a given key operation.
+ * @hide
*/
@VintfStability
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
diff --git a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
index 33f71b8..2d9d0ff 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -20,6 +20,7 @@
* Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to
* authentiate the user, and in KeyCharacteristics to specify the allowable mechanisms for
* authenticating to activate a key.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 384416e..17aab25 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -211,6 +211,7 @@
* hardwareEnforced authorization list. Tag::OS_VERSION, Tag::OS_PATCHLEVEL,
* Tag::VENDOR_PATCHLEVEL, and Tag::BOOT_PATCHLEVEL must be cryptographically bound to every
* IKeyMintDevice key, as described in the Key Access Control section above.
+ * @hide
*/
@VintfStability
interface IKeyMintDevice {
@@ -761,6 +762,27 @@
*/
void earlyBootEnded();
+ /*
+ * Called by the client to get a wrapped per-boot ephemeral key from a wrapped storage key.
+ * Clients will then use the returned per-boot ephemeral key in place of the wrapped storage
+ * key. Whenever the hardware is presented with a per-boot ephemeral key for an operation, it
+ * must use the storage key associated with that ephemeral key to perform the requested
+ * operation.
+ *
+ * Implementations should return ErrorCode::UNIMPLEMENTED if they don't support wrapped storage
+ * keys.
+ *
+ * Implementations should return ErrorCode::INVALID_ARGUMENT (as a ServiceSpecificException)
+ * if the input key blob doesn't represent a valid long-lived wrapped storage key.
+ *
+ * @param storageKeyBlob is the wrapped storage key for which the client wants a per-boot
+ * ephemeral key
+ *
+ * @return a buffer containing the per-boot ephemeral keyblob that should henceforth be used in
+ * place of the input storageKeyBlob
+ */
+ byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
+
/**
* Called by the client to perform a KeyMint operation.
*
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index 1c2511b..5ad54cd 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -20,6 +20,7 @@
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.secureclock.TimeStampToken;
+/** @hide */
@VintfStability
interface IKeyMintOperation {
/**
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 1b09e9d..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;
@@ -109,6 +110,7 @@
* The IRemotelyProvisionedComponent supports a test mode, allowing the generation of test key pairs
* and test CertificateRequests. Test keys/requests are annotated as such, and the BCC used for test
* CertificateRequests must contain freshly-generated keys, not the real BCC key pairs.
+ * @hide
*/
@VintfStability
interface IRemotelyProvisionedComponent {
@@ -165,7 +167,7 @@
* protected: bstr .cbor {
* 1 : -8, // Algorithm : EdDSA
* },
- * unprotected: bstr .size 0
+ * unprotected: { },
* payload: bstr .cbor SignatureKey,
* signature: bstr PureEd25519(.cbor SignatureKeySignatureInput)
* ]
@@ -190,7 +192,7 @@
* protected: bstr .cbor {
* 1 : -8, // Algorithm : EdDSA
* },
- * unprotected: bstr .size 0
+ * unprotected: { },
* payload: bstr .cbor Eek,
* signature: bstr PureEd25519(.cbor EekSignatureInput)
* ]
@@ -200,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 = [
@@ -219,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.
*
@@ -239,7 +241,7 @@
* protected : bstr .cbor {
* 1 : 5, // Algorithm : HMAC-256
* },
- * unprotected : bstr .size 0,
+ * unprotected : { },
* // Payload is PublicKeys from keysToSign argument, in provided order.
* payload: bstr .cbor [ * PublicKey ],
* tag: bstr
@@ -256,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/KeyCharacteristics.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
index 3a32e4d..25fdee3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -28,6 +28,7 @@
* enforced. Note that enforcement at a given security level means that the semantics of the tag
* and value are fully enforced. See the definition of individual tags for specifications of what
* must be enforced.
+ * @hide
*/
@VintfStability
parcelable KeyCharacteristics {
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
index 69bec2d7..f3c5477 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -22,6 +22,7 @@
/**
* This structure is returned when a new key is created with generateKey(), importKey() or
* importWrappedKey().
+ * @hide
*/
@VintfStability
parcelable KeyCreationResult {
@@ -52,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/KeyFormat.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
index 6ad8e3d..da3d521 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
@@ -18,6 +18,7 @@
/**
* Formats for key import and export.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
index ae0d152..8da7578 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -20,6 +20,7 @@
/**
* KeyMintHardwareInfo is the hardware information returned by calling KeyMint getHardwareInfo()
+ * @hide
*/
@VintfStability
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
index 0cd53c2..f896125 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
@@ -21,6 +21,7 @@
* either the hardware-enforced or software-enforced list for a key, indicating whether the key is
* hardware or software-based. Specifically, a key with GENERATED in the hardware-enforced list
* must be guaranteed never to have existed outide the secure hardware.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
index bf6c9b2..b69e678 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
@@ -22,6 +22,7 @@
/**
* Identifies the key authorization parameters to be used with keyMint. This is usually
* provided as an array of KeyParameters to IKeyMintDevice or Operation.
+ * @hide
*/
@VintfStability
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
index a4f5154..59016f2 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
@@ -26,10 +26,10 @@
import android.hardware.security.keymint.PaddingMode;
import android.hardware.security.keymint.SecurityLevel;
+/** @hide */
@VintfStability
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
union KeyParameterValue {
-
/* Represents an invalid value type. */
int invalid;
@@ -45,7 +45,7 @@
SecurityLevel securityLevel;
/* Other types */
- boolean boolValue; // Always true, if present.
+ boolean boolValue; // Always true, if present.
int integer;
long longInteger;
long dateTime;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
index 978a027..c874fc3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -18,6 +18,7 @@
/**
* Possible purposes of a key (or pair).
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
index da85a50..62a48e9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -19,26 +19,28 @@
/**
* MacedPublicKey contains a CBOR-encoded public key, MACed by an IRemotelyProvisionedComponent, to
* prove that the key pair was generated by that component.
+ * @hide
*/
@VintfStability
parcelable MacedPublicKey {
/**
* 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
- * unprotected: bstr .size 0,
+ * unprotected: { },
* payload : bstr .cbor PublicKey,
* tag : bstr HMAC-256(K_mac, MAC_structure)
* ]
*
* 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.
* },
@@ -50,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/PaddingMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
index 80b73bd..fbb373b 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
@@ -23,6 +23,7 @@
* padding modes for both symmetric and asymmetric algorithms. Note that implementations should not
* provide all possible combinations of algorithm and padding, only the
* cryptographically-appropriate pairs.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
index 1ec3bf0..5199062 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -19,6 +19,7 @@
/**
* ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
* authenticate the keysToSign (see keysToSignMac output argument).
+ * @hide
*/
@VintfStability
parcelable ProtectedData {
@@ -32,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 {
@@ -80,7 +81,7 @@
* bstr .cbor { // Protected params
* 1 : -8, // Algorithm : EdDSA
* },
- * bstr .size 0, // Unprotected params
+ * { }, // Unprotected params
* bstr .size 32, // MAC key
* bstr PureEd25519(DK_priv, .cbor SignedMac_structure)
* ]
@@ -127,7 +128,7 @@
* protected: bstr .cbor {
* 1 : -8, // Algorithm : EdDSA
* },
- * unprotected: bstr .size 0,
+ * unprotected: { },
* payload: bstr .cbor BccPayload,
* // First entry in the chain is signed by DK_pub, the others are each signed by their
* // immediate predecessor. See RFC 8032 for signature representation.
diff --git a/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
index ecbde8c..80c63b2 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
@@ -27,6 +27,7 @@
* certificates. This specifies the security level of the weakest environment involved in
* enforcing that particular tag, i.e. the sort of security environment an attacker would have
* to subvert in order to break the enforcement of that tag.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index aa9aa6f..6243bb9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -25,6 +25,7 @@
/**
* Tag specifies various kinds of tags that can be set in KeyParameter to identify what kind of
* data are stored in KeyParameter.
+ * @hide
*/
@VintfStability
@Backing(type="int")
diff --git a/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
index a273af3..1ba6ede 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
@@ -18,6 +18,7 @@
/**
* TagType classifies Tags in Tag.aidl into various groups of data.
+ * @hide
*/
@VintfStability
@Backing(type="int")
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 2373b26..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 */);
@@ -156,7 +171,7 @@
}
auto protectedParms = macedKeyItem->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
- auto unprotectedParms = macedKeyItem->asArray()->get(kCoseMac0UnprotectedParams)->asBstr();
+ auto unprotectedParms = macedKeyItem->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr();
auto tag = macedKeyItem->asArray()->get(kCoseMac0Tag)->asBstr();
if (!protectedParms || !unprotectedParms || !payload || !tag) {
@@ -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..1e907db 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);
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..86bc9c4 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,6 +276,9 @@
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) \
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index db53a8f..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)->asBstr();
- ASSERT_NE(unprotParms, nullptr);
- ASSERT_EQ(unprotParms->value().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)->asBstr();
- ASSERT_NE(unprotParms, nullptr);
- ASSERT_EQ(unprotParms->value().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::Bstr()) // 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::Bstr()) // 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/cppcose.cpp b/security/keymint/support/cppcose.cpp
index c626ade..bafb2b6 100644
--- a/security/keymint/support/cppcose.cpp
+++ b/security/keymint/support/cppcose.cpp
@@ -85,7 +85,7 @@
return cppbor::Array()
.add(cppbor::Map().add(ALGORITHM, HMAC_256).canonicalize().encode())
- .add(cppbor::Bstr() /* unprotected */)
+ .add(cppbor::Map() /* unprotected */)
.add(payload)
.add(tag.moveValue());
}
@@ -97,7 +97,7 @@
}
auto protectedParms = mac->get(kCoseMac0ProtectedParams)->asBstr();
- auto unprotectedParms = mac->get(kCoseMac0UnprotectedParams)->asBstr();
+ auto unprotectedParms = mac->get(kCoseMac0UnprotectedParams)->asMap();
auto payload = mac->get(kCoseMac0Payload)->asBstr();
auto tag = mac->get(kCoseMac0Tag)->asBstr();
if (!protectedParms || !unprotectedParms || !payload || !tag) {
@@ -115,7 +115,7 @@
}
auto protectedParms = mac->get(kCoseMac0ProtectedParams)->asBstr();
- auto unprotectedParms = mac->get(kCoseMac0UnprotectedParams)->asBstr();
+ auto unprotectedParms = mac->get(kCoseMac0UnprotectedParams)->asMap();
auto payload = mac->get(kCoseMac0Payload)->asBstr();
auto tag = mac->get(kCoseMac0Tag)->asBstr();
if (!protectedParms || !unprotectedParms || !payload || !tag) {
@@ -168,7 +168,7 @@
return cppbor::Array()
.add(protParms)
- .add(bytevec{} /* unprotected parameters */)
+ .add(cppbor::Map() /* unprotected parameters */)
.add(payload)
.add(*signature);
}
@@ -185,7 +185,7 @@
}
const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
- const cppbor::Bstr* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->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();
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 111cb30..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);
@@ -83,7 +85,7 @@
}
const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
- const cppbor::Bstr* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->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();
diff --git a/security/secureclock/aidl/Android.bp b/security/secureclock/aidl/Android.bp
index c8e5c02..c78be3b 100644
--- a/security/secureclock/aidl/Android.bp
+++ b/security/secureclock/aidl/Android.bp
@@ -16,7 +16,8 @@
stability: "vintf",
backend: {
java: {
- sdk_version: "module_current",
+ platform_apis: true,
+ srcs_available: true,
},
ndk: {
vndk: {
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl
index 3778897..4ecc1e4 100644
--- a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl
@@ -11,7 +11,8 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* limitations under the License.
- *////////////////////////////////////////////////////////////////////////////////
+ */
+///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
@@ -30,6 +31,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.secureclock;
+/* @hide */
@VintfStability
interface ISecureClock {
android.hardware.security.secureclock.TimeStampToken generateTimeStamp(in long challenge);
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
index 00a8bb2..d105ac8 100644
--- a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.secureclock;
+/* @hide */
@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
parcelable TimeStampToken {
long challenge;
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
index bebeb5c..2e0e389 100644
--- a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.secureclock;
+/* @hide */
@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
parcelable Timestamp {
long milliSeconds;
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
index 577dd8f..a742ff0 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
@@ -25,8 +25,8 @@
* secret. The shared secret must be available to secure clock service by implementing
* ISharedSecret aidl. Note: ISecureClock depends on the shared secret, without which the secure
* time stamp token cannot be generated.
+ * @hide
*/
-
@VintfStability
interface ISecureClock {
/**
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
index dd95732..71b4278 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -20,8 +20,8 @@
/**
* TimeStampToken instances are used for secure environments that requires secure time information.
+ * @hide
*/
-
@VintfStability
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
parcelable TimeStampToken {
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
index 27758e1..5061aa4 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
@@ -21,6 +21,7 @@
* and a secure environment's notion of "current time" must not repeat until the Android device
* reboots, or until at least 50 million years have elapsed (note that this requirement is satisfied
* by setting the clock to zero during each boot, and then counting time accurately).
+ * @hide
*/
@VintfStability
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
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/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/ISharedSecret.aidl b/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/ISharedSecret.aidl
index 2509936..e76efe7 100644
--- a/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/ISharedSecret.aidl
+++ b/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/ISharedSecret.aidl
@@ -1,3 +1,17 @@
+/*
+ * 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.
+ * limitations under the License.
+ */
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
@@ -17,6 +31,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.sharedsecret;
+/* @hide */
@VintfStability
interface ISharedSecret {
android.hardware.security.sharedsecret.SharedSecretParameters getSharedSecretParameters();
diff --git a/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/SharedSecretParameters.aidl b/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
index 9b65046..e15fd49 100644
--- a/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
+++ b/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
@@ -1,3 +1,18 @@
+/*
+ * 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -17,7 +32,8 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.security.sharedsecret;
-@VintfStability
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
parcelable SharedSecretParameters {
byte[] seed;
byte[] nonce;
diff --git a/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl
index 906303f..eca8d87 100644
--- a/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl
+++ b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl
@@ -22,8 +22,8 @@
* An ISharedSecret enables any service that implements this interface to establish a shared secret
* with one or more other services such as ISecureClock, TEE IKeymintDevice, StrongBox
* IKeymintDevice, etc. The shared secret is a 256-bit HMAC key and it is further used to generate
- * secure tokens with integrity protection. There are two steps to establish a shared secret between
- * the collaborating services:
+ * secure tokens with integrity protection. There are three steps to establish a shared secret
+ * between the collaborating services:
*
* Step 1: During Android startup the system calls each service that implements this interface to
* get the shared secret parameters. This is done using getSharedSecretParameters method defined
@@ -35,8 +35,8 @@
* Step 3: The system collects sharing check hash values from each service and evaluates them. If
* they are all equal, then the shared secret generation is considered to be successful else it is
* considered to have failed.
+ * @hide
*/
-
@VintfStability
interface ISharedSecret {
/**
@@ -64,11 +64,11 @@
/**
* This method is the second and final step in the process for agreeing on a shared key. It is
- * called by Android during startup. The system calls it on each of the keymint services, and
- * sends to it all of the SharedSecretParameters returned by all keymint services.
+ * called by Android during startup. The system calls it on each of the HAL instances, and
+ * sends to it all of the SharedSecretParameters returned by all HAL instances.
*
- * This method computes the shared 32-byte HMAC key ``H'' as follows (all keymint services
- * instances perform the same computation to arrive at the same result):
+ * This method computes the shared 32-byte HMAC key ``H'' as follows (all HAL instances perform
+ * the same computation to arrive at the same result):
*
* H = CKDF(key = K,
* context = P1 || P2 || ... || Pn,
@@ -98,16 +98,16 @@
* Note that the label "KeymasterSharedMac" is the 18-byte UTF-8 encoding of the string.
*
* @param params is an array of SharedSecretParameters The lexicographically sorted
- * SharedSecretParameters data returned by all keymint services when getSharedSecretParameters
+ * SharedSecretParameters data returned by all HAL instances when getSharedSecretParameters
* was called.
*
- * @return sharingCheck A 32-byte value used to verify that all the keymint services have
+ * @return sharingCheck A 32-byte value used to verify that all the HAL instances have
* computed the same shared HMAC key. The sharingCheck value is computed as follows:
*
* sharingCheck = HMAC(H, KEY_CHECK_LABEL)
*
* The string is UTF-8 encoded, 27 bytes in length. If the returned values of all
- * keymint services don't match, clients must assume that HMAC agreement
+ * HAL instances don't match, clients must assume that HMAC agreement
* failed.
*/
byte[] computeSharedSecret(in SharedSecretParameters[] params);
diff --git a/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
index 691b3f1..8144699 100644
--- a/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
+++ b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
@@ -21,9 +21,10 @@
* HMAC key between multiple keymint services. These parameters are returned in by
* getSharedSecretParameters() and send to computeShareSecret(). See the named methods in
* ISharedSecret for details of usage.
+ * @hide
*/
-
@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
parcelable SharedSecretParameters {
/**
* Either empty or contains a non zero persistent value that is associated with the pre-shared
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/soundtrigger/2.0/default/OWNERS b/soundtrigger/2.0/default/OWNERS
index 6fdc97c..ed739cf 100644
--- a/soundtrigger/2.0/default/OWNERS
+++ b/soundtrigger/2.0/default/OWNERS
@@ -1,3 +1,3 @@
elaurent@google.com
-krocard@google.com
mnaganov@google.com
+ytai@google.com
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());
+ }
+}
diff --git a/tv/input/1.0/vts/functional/VtsHalTvInputV1_0TargetTest.cpp b/tv/input/1.0/vts/functional/VtsHalTvInputV1_0TargetTest.cpp
index 8092d5e..15ea3e9 100644
--- a/tv/input/1.0/vts/functional/VtsHalTvInputV1_0TargetTest.cpp
+++ b/tv/input/1.0/vts/functional/VtsHalTvInputV1_0TargetTest.cpp
@@ -313,6 +313,9 @@
tv_input_->openStream(device_id, stream_id,
[&result](Result res, const native_handle_t*) { result = res; });
EXPECT_EQ(Result::INVALID_STATE, result);
+
+ // close stream as subsequent tests assume no open streams
+ EXPECT_EQ(Result::OK, tv_input_->closeStream(device_id, stream_id));
}
/*
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.cpp b/tv/tuner/1.0/vts/functional/FilterTests.cpp
index 1a09290..f470245 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FilterTests.cpp
@@ -70,10 +70,6 @@
}
bool FilterCallback::readFilterEventData() {
- if (mFilterMQ == NULL) {
- ALOGW("[vts] FMQ is not configured and does not need to be tested.");
- return true;
- }
bool result = false;
DemuxFilterEvent filterEvent = mFilterEvent;
ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId);
@@ -95,16 +91,19 @@
}
// EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not
// match";
-
- mDataOutputBuffer.resize(mDataLength);
- result = mFilterMQ->read(mDataOutputBuffer.data(), mDataLength);
- EXPECT_TRUE(result) << "can't read from Filter MQ";
+ if (mFilterMQ != NULL) {
+ mDataOutputBuffer.resize(mDataLength);
+ result = mFilterMQ->read(mDataOutputBuffer.data(), mDataLength);
+ EXPECT_TRUE(result) << "can't read from Filter MQ";
+ }
/*for (int i = 0; i < mDataLength; i++) {
EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
}*/
}
- mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+ if (mFilterMQ != NULL) {
+ mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+ }
return result;
}
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index b0a9c03..834e296 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -221,7 +221,7 @@
frontendArray[DVBT].tuneStatusTypes = types;
frontendArray[DVBT].expectTuneStatuses = statuses;
frontendArray[DVBT].isSoftwareFe = true;
- frontendArray[DVBS].enable = true;
+ frontendArray[DVBT].enable = true;
frontendArray[DVBS].type = FrontendType::DVBS;
frontendArray[DVBS].enable = true;
frontendArray[DVBS].isSoftwareFe = true;
diff --git a/tv/tuner/1.1/default/Android.bp b/tv/tuner/1.1/default/Android.bp
index 86025cf..a612802 100644
--- a/tv/tuner/1.1/default/Android.bp
+++ b/tv/tuner/1.1/default/Android.bp
@@ -31,6 +31,7 @@
"android.hardware.tv.tuner@1.1",
"android.hidl.memory@1.0",
"libcutils",
+ "libdmabufheap",
"libfmq",
"libhidlbase",
"libhidlmemory",
diff --git a/tv/tuner/1.1/default/Filter.cpp b/tv/tuner/1.1/default/Filter.cpp
index 5ddac99..7d609ea 100644
--- a/tv/tuner/1.1/default/Filter.cpp
+++ b/tv/tuner/1.1/default/Filter.cpp
@@ -16,9 +16,11 @@
#define LOG_TAG "android.hardware.tv.tuner@1.1-Filter"
-#include "Filter.h"
+#include <BufferAllocator/BufferAllocator.h>
#include <utils/Log.h>
+#include "Filter.h"
+
namespace android {
namespace hardware {
namespace tv {
@@ -829,15 +831,15 @@
}
int Filter::createAvIonFd(int size) {
- // Create an ion fd and allocate an av fd mapped to a buffer to it.
- int ion_fd = ion_open();
- if (ion_fd == -1) {
- ALOGE("[Filter] Failed to open ion fd %d", errno);
+ // Create an DMA-BUF fd and allocate an av fd mapped to a buffer to it.
+ auto buffer_allocator = std::make_unique<BufferAllocator>();
+ if (!buffer_allocator) {
+ ALOGE("[Filter] Unable to create BufferAllocator object");
return -1;
}
int av_fd = -1;
- ion_alloc_fd(dup(ion_fd), size, 0 /*align*/, ION_HEAP_SYSTEM_MASK, 0 /*flags*/, &av_fd);
- if (av_fd == -1) {
+ av_fd = buffer_allocator->Alloc("system-uncached", size);
+ if (av_fd < 0) {
ALOGE("[Filter] Failed to create av fd %d", errno);
return -1;
}
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.cpp b/tv/tuner/1.1/vts/functional/FilterTests.cpp
index 1617642..3bcf32a 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.1/vts/functional/FilterTests.cpp
@@ -252,7 +252,11 @@
return AssertionResult(status == Result::SUCCESS);
}
-AssertionResult FilterTests::getFilterMQDescriptor(uint64_t filterId) {
+AssertionResult FilterTests::getFilterMQDescriptor(uint64_t filterId, bool getMqDesc) {
+ if (!getMqDesc) {
+ ALOGE("[vts] Filter does not need FMQ.");
+ return success();
+ }
Result status;
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.h b/tv/tuner/1.1/vts/functional/FilterTests.h
index 6749265..59611fa 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.h
+++ b/tv/tuner/1.1/vts/functional/FilterTests.h
@@ -161,7 +161,7 @@
AssertionResult configAvFilterStreamType(AvStreamType type, uint64_t filterId);
AssertionResult configIpFilterCid(uint32_t ipCid, uint64_t filterId);
AssertionResult configureMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes);
- AssertionResult getFilterMQDescriptor(uint64_t filterId);
+ AssertionResult getFilterMQDescriptor(uint64_t filterId, bool getMqDesc);
AssertionResult startFilter(uint64_t filterId);
AssertionResult stopFilter(uint64_t filterId);
AssertionResult closeFilter(uint64_t filterId);
diff --git a/tv/tuner/1.1/vts/functional/FrontendTests.cpp b/tv/tuner/1.1/vts/functional/FrontendTests.cpp
index 887f8b8..0fd5be0 100644
--- a/tv/tuner/1.1/vts/functional/FrontendTests.cpp
+++ b/tv/tuner/1.1/vts/functional/FrontendTests.cpp
@@ -130,7 +130,7 @@
return;
}
- uint32_t targetFrequency = getTargetFrequency(config.settings, config.type);
+ uint32_t targetFrequency = getTargetFrequency(config.settings);
if (type == FrontendScanType::SCAN_BLIND) {
// reset the frequency in the scan configuration to test blind scan. The settings param of
// passed in means the real input config on the transponder connected to the DUT.
@@ -184,64 +184,59 @@
mScanMsgProcessed = true;
}
-uint32_t FrontendCallback::getTargetFrequency(FrontendSettings settings, FrontendType type) {
- switch (type) {
- case FrontendType::ANALOG:
+uint32_t FrontendCallback::getTargetFrequency(FrontendSettings settings) {
+ switch (settings.getDiscriminator()) {
+ case FrontendSettings::hidl_discriminator::analog:
return settings.analog().frequency;
- case FrontendType::ATSC:
+ case FrontendSettings::hidl_discriminator::atsc:
return settings.atsc().frequency;
- case FrontendType::ATSC3:
+ case FrontendSettings::hidl_discriminator::atsc3:
return settings.atsc3().frequency;
- case FrontendType::DVBC:
+ case FrontendSettings::hidl_discriminator::dvbc:
return settings.dvbc().frequency;
- case FrontendType::DVBS:
+ case FrontendSettings::hidl_discriminator::dvbs:
return settings.dvbs().frequency;
- case FrontendType::DVBT:
+ case FrontendSettings::hidl_discriminator::dvbt:
return settings.dvbt().frequency;
- case FrontendType::ISDBS:
+ case FrontendSettings::hidl_discriminator::isdbs:
return settings.isdbs().frequency;
- case FrontendType::ISDBS3:
+ case FrontendSettings::hidl_discriminator::isdbs3:
return settings.isdbs3().frequency;
- case FrontendType::ISDBT:
+ case FrontendSettings::hidl_discriminator::isdbt:
return settings.isdbt().frequency;
- default:
- return 0;
}
}
void FrontendCallback::resetBlindScanStartingFrequency(FrontendConfig& config,
uint32_t resetingFreq) {
- switch (config.type) {
- case FrontendType::ANALOG:
+ switch (config.settings.getDiscriminator()) {
+ case FrontendSettings::hidl_discriminator::analog:
config.settings.analog().frequency = resetingFreq;
break;
- case FrontendType::ATSC:
+ case FrontendSettings::hidl_discriminator::atsc:
config.settings.atsc().frequency = resetingFreq;
break;
- case FrontendType::ATSC3:
+ case FrontendSettings::hidl_discriminator::atsc3:
config.settings.atsc3().frequency = resetingFreq;
break;
- case FrontendType::DVBC:
+ case FrontendSettings::hidl_discriminator::dvbc:
config.settings.dvbc().frequency = resetingFreq;
break;
- case FrontendType::DVBS:
+ case FrontendSettings::hidl_discriminator::dvbs:
config.settings.dvbs().frequency = resetingFreq;
break;
- case FrontendType::DVBT:
+ case FrontendSettings::hidl_discriminator::dvbt:
config.settings.dvbt().frequency = resetingFreq;
break;
- case FrontendType::ISDBS:
+ case FrontendSettings::hidl_discriminator::isdbs:
config.settings.isdbs().frequency = resetingFreq;
break;
- case FrontendType::ISDBS3:
+ case FrontendSettings::hidl_discriminator::isdbs3:
config.settings.isdbs3().frequency = resetingFreq;
break;
- case FrontendType::ISDBT:
+ case FrontendSettings::hidl_discriminator::isdbt:
config.settings.isdbt().frequency = resetingFreq;
break;
- default:
- // do nothing
- return;
}
}
@@ -490,6 +485,9 @@
}
void FrontendTests::tuneTest(FrontendConfig frontendConf) {
+ if (!frontendConf.enable) {
+ return;
+ }
uint32_t feId;
getFrontendIdByType(frontendConf.type, feId);
ASSERT_TRUE(feId != INVALID_ID);
@@ -506,6 +504,9 @@
}
void FrontendTests::scanTest(FrontendConfig frontendConf, FrontendScanType scanType) {
+ if (!frontendConf.enable) {
+ return;
+ }
uint32_t feId;
getFrontendIdByType(frontendConf.type, feId);
ASSERT_TRUE(feId != INVALID_ID);
diff --git a/tv/tuner/1.1/vts/functional/FrontendTests.h b/tv/tuner/1.1/vts/functional/FrontendTests.h
index 43c1579..01d2007 100644
--- a/tv/tuner/1.1/vts/functional/FrontendTests.h
+++ b/tv/tuner/1.1/vts/functional/FrontendTests.h
@@ -82,7 +82,7 @@
void scanTest(sp<IFrontend>& frontend, FrontendConfig config, FrontendScanType type);
// Helper methods
- uint32_t getTargetFrequency(FrontendSettings settings, FrontendType type);
+ uint32_t getTargetFrequency(FrontendSettings settings);
void resetBlindScanStartingFrequency(FrontendConfig& config, uint32_t resetingFreq);
private:
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
index 2e6c87f..97fb90d 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
@@ -28,6 +28,10 @@
void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf,
FrontendConfig frontendConf) {
+ if (!frontendConf.enable) {
+ return;
+ }
+
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -49,7 +53,7 @@
if (filterConf.monitorEventTypes > 0) {
ASSERT_TRUE(mFilterTests.configureMonitorEvent(filterId, filterConf.monitorEventTypes));
}
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -60,6 +64,10 @@
void TunerFilterHidlTest::reconfigSingleFilterInDemuxTest(FilterConfig filterConf,
FilterConfig filterReconf,
FrontendConfig frontendConf) {
+ if (!frontendConf.enable) {
+ return;
+ }
+
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -76,7 +84,7 @@
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterReconf.settings, filterId));
@@ -92,6 +100,10 @@
void TunerBroadcastHidlTest::mediaFilterUsingSharedMemoryTest(FilterConfig filterConf,
FrontendConfig frontendConf) {
+ if (!frontendConf.enable) {
+ return;
+ }
+
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -110,7 +122,7 @@
ASSERT_TRUE(mFilterTests.getSharedAvMemoryHandle(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
ASSERT_TRUE(mFilterTests.configAvFilterStreamType(filterConf.streamType, filterId));
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
// tune test
ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
@@ -125,6 +137,10 @@
void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
FrontendConfig frontendConf, DvrConfig dvrConf) {
+ if (!frontendConf.enable) {
+ return;
+ }
+
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -146,7 +162,7 @@
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
filter = mFilterTests.getFilterById(filterId);
ASSERT_TRUE(filter != nullptr);
mDvrTests.startRecordOutputThread(dvrConf.settings.record());
@@ -170,40 +186,41 @@
TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
description("Open and start a filter in Demux.");
// TODO use parameterized tests
- configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+ configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
}
TEST_P(TunerFilterHidlTest, ConfigIpFilterInDemuxWithCid) {
description("Open and configure an ip filter in Demux.");
// TODO use parameterized tests
- configSingleFilterInDemuxTest(filterArray[IP_IP0], frontendArray[DVBT]);
+ configSingleFilterInDemuxTest(filterArray[IP_IP0], frontendArray[defaultFrontend]);
}
TEST_P(TunerFilterHidlTest, ReconfigFilterToReceiveStartId) {
description("Recofigure and restart a filter to test start id.");
// TODO use parameterized tests
reconfigSingleFilterInDemuxTest(filterArray[TS_VIDEO0], filterArray[TS_VIDEO1],
- frontendArray[DVBT]);
+ frontendArray[defaultFrontend]);
}
TEST_P(TunerRecordHidlTest, RecordDataFlowWithTsRecordFilterTest) {
description("Feed ts data from frontend to recording and test with ts record filter");
- recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
+ recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[defaultFrontend],
+ dvrArray[DVR_RECORD0]);
}
TEST_P(TunerFrontendHidlTest, TuneFrontendWithFrontendSettingsExt1_1) {
description("Tune one Frontend with v1_1 extended setting and check Lock event");
- mFrontendTests.tuneTest(frontendArray[DVBT]);
+ mFrontendTests.tuneTest(frontendArray[defaultFrontend]);
}
TEST_P(TunerFrontendHidlTest, BlindScanFrontendWithEndFrequency) {
description("Run an blind frontend scan with v1_1 extended setting and check lock scanMessage");
- mFrontendTests.scanTest(frontendScanArray[SCAN_DVBT], FrontendScanType::SCAN_BLIND);
+ mFrontendTests.scanTest(frontendScanArray[defaultScanFrontend], FrontendScanType::SCAN_BLIND);
}
TEST_P(TunerBroadcastHidlTest, MediaFilterWithSharedMemoryHandle) {
description("Test the Media Filter with shared memory handle");
- mediaFilterUsingSharedMemoryTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+ mediaFilterUsingSharedMemoryTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
}
TEST_P(TunerFrontendHidlTest, GetFrontendDtmbCaps) {
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
index ecdf683..ad57849 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
@@ -55,6 +55,7 @@
using namespace std;
+const uint32_t FMQ_SIZE_512K = 0x80000;
const uint32_t FMQ_SIZE_1M = 0x100000;
const uint32_t FMQ_SIZE_4M = 0x400000;
const uint32_t FMQ_SIZE_16M = 0x1000000;
@@ -94,6 +95,7 @@
uint32_t bufferSize;
DemuxFilterType type;
DemuxFilterSettings settings;
+ bool getMqDesc;
AvStreamType streamType;
uint32_t ipCid;
uint32_t monitorEventTypes;
@@ -102,6 +104,7 @@
};
struct FrontendConfig {
+ bool enable;
bool isSoftwareFe;
bool canConnectToCiCam;
uint32_t ciCamId;
@@ -124,6 +127,7 @@
static FilterConfig filterArray[FILTER_MAX];
static DvrConfig dvrArray[DVR_MAX];
static int defaultFrontend = DVBT;
+static int defaultScanFrontend = SCAN_DVBT;
/** Configuration array for the frontend tune test */
inline void initFrontendConfig() {
@@ -159,8 +163,10 @@
.transmissionMode =
android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode::MODE_8K_E,
});
+ frontendArray[DVBT].enable = true;
frontendArray[DVBS].type = FrontendType::DVBS;
frontendArray[DVBS].isSoftwareFe = true;
+ frontendArray[DVBS].enable = true;
};
/** Configuration array for the frontend scan test */
@@ -223,6 +229,7 @@
.isRaw = false,
.streamId = 0xbd,
});
+ filterArray[TS_PES0].getMqDesc = true;
// TS PCR filter setting
filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR);
@@ -243,6 +250,7 @@
filterArray[TS_SECTION0].settings.ts().filterSettings.section({
.isRaw = false,
});
+ filterArray[TS_SECTION0].getMqDesc = true;
// TS RECORD filter setting
filterArray[TS_RECORD0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_RECORD0].type.subType.tsFilterType(DemuxTsFilterType::RECORD);
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/ActivePwle.aidl
similarity index 87%
copy from biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
copy to vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/ActivePwle.aidl
index c5288fd..de3ad3c 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/ActivePwle.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -31,10 +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.common;
+package android.hardware.vibrator;
@VintfStability
-parcelable HardwareInfo {
- String deviceName;
- String hardwareVersion;
- String serialNumber;
+parcelable ActivePwle {
+ float startAmplitude;
+ float startFrequency;
+ float endAmplitude;
+ float endFrequency;
+ int duration;
}
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/Braking.aidl
similarity index 87%
copy from biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
copy to vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/Braking.aidl
index c5288fd..d38c584 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/Braking.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -31,10 +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.common;
-@VintfStability
-parcelable HardwareInfo {
- String deviceName;
- String hardwareVersion;
- String serialNumber;
+package android.hardware.vibrator;
+@Backing(type="int") @VintfStability
+enum Braking {
+ NONE = 0,
+ CLAB = 1,
}
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/BrakingPwle.aidl
similarity index 88%
copy from biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
copy to vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/BrakingPwle.aidl
index c5288fd..fa7b43a 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/BrakingPwle.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -31,10 +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.common;
+package android.hardware.vibrator;
@VintfStability
-parcelable HardwareInfo {
- String deviceName;
- String hardwareVersion;
- String serialNumber;
+parcelable BrakingPwle {
+ android.hardware.vibrator.Braking braking;
+ int duration;
}
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
index 0995d2d..3be58a1 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositePrimitive.aidl
index 0b4b527..50de13f 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositePrimitive.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositePrimitive.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/Effect.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/Effect.aidl
index 0d2b340..adf0f20 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/Effect.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/Effect.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/EffectStrength.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/EffectStrength.aidl
index 808644a..af5e158 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/EffectStrength.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/EffectStrength.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
index 1f2d946..b7afb66 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -50,6 +51,13 @@
void alwaysOnDisable(in int id);
float getResonantFrequency();
float getQFactor();
+ float getFrequencyResolution();
+ float getFrequencyMinimum();
+ float[] getBandwidthAmplitudeMap();
+ int getPwlePrimitiveDurationMax();
+ int getPwleCompositionSizeMax();
+ android.hardware.vibrator.Braking[] getSupportedBraking();
+ void composePwle(in android.hardware.vibrator.PrimitivePwle[] composite, in android.hardware.vibrator.IVibratorCallback callback);
const int CAP_ON_CALLBACK = 1;
const int CAP_PERFORM_CALLBACK = 2;
const int CAP_AMPLITUDE_CONTROL = 4;
@@ -59,4 +67,6 @@
const int CAP_ALWAYS_ON_CONTROL = 64;
const int CAP_GET_RESONANT_FREQUENCY = 128;
const int CAP_GET_Q_FACTOR = 256;
+ const int CAP_FREQUENCY_CONTROL = 512;
+ const int CAP_COMPOSE_PWLE_EFFECTS = 1024;
}
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorCallback.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorCallback.aidl
index f99ecc1..99d6d22 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorCallback.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorCallback.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
index 8e3ac88..290c68d 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
@@ -12,7 +12,8 @@
* 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PrimitivePwle.aidl
similarity index 88%
copy from biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
copy to vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PrimitivePwle.aidl
index c5288fd..584bcf4 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PrimitivePwle.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -31,10 +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.common;
+package android.hardware.vibrator;
@VintfStability
-parcelable HardwareInfo {
- String deviceName;
- String hardwareVersion;
- String serialNumber;
+union PrimitivePwle {
+ android.hardware.vibrator.ActivePwle active;
+ android.hardware.vibrator.BrakingPwle braking;
}
diff --git a/vibrator/aidl/android/hardware/vibrator/ActivePwle.aidl b/vibrator/aidl/android/hardware/vibrator/ActivePwle.aidl
new file mode 100644
index 0000000..fd5f8d1
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/ActivePwle.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.vibrator;
+
+@VintfStability
+parcelable ActivePwle {
+ /**
+ * Amplitude ranging from 0.0 (inclusive) to 1.0 (inclusive)
+ * in units of output acceleration amplitude, not voltage amplitude.
+ *
+ * 0.0 represents no output acceleration amplitude
+ * 1.0 represents maximum output acceleration amplitude at resonant frequency
+ */
+ float startAmplitude;
+ /**
+ * Absolute frequency point in the units of hertz
+ */
+ float startFrequency;
+ /**
+ * Amplitude ranging from 0.0 (inclusive) to 1.0 (inclusive)
+ * in units of output acceleration amplitude, not voltage amplitude.
+ *
+ * 0.0 represents no output acceleration amplitude
+ * 1.0 represents maximum output acceleration amplitude at resonant frequency
+ */
+ float endAmplitude;
+ /**
+ * Absolute frequency point in the units of hertz
+ */
+ float endFrequency;
+ /**
+ * Total duration from start point to end point in the units of milliseconds
+ */
+ int duration;
+}
diff --git a/biometrics/common/aidl/android/hardware/biometrics/common/HardwareInfo.aidl b/vibrator/aidl/android/hardware/vibrator/Braking.aidl
similarity index 60%
copy from biometrics/common/aidl/android/hardware/biometrics/common/HardwareInfo.aidl
copy to vibrator/aidl/android/hardware/vibrator/Braking.aidl
index ce9e354..2bc51db 100644
--- a/biometrics/common/aidl/android/hardware/biometrics/common/HardwareInfo.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/Braking.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,22 +14,21 @@
* limitations under the License.
*/
-package android.hardware.biometrics.common;
+package android.hardware.vibrator;
@VintfStability
-parcelable HardwareInfo {
+@Backing(type="int")
+enum Braking {
/**
- * An identifier uniquely identifying a subsystem.
+ * No braking mechanism used.
+ * This is the default if the hardware does not support any braking mechanism.
*/
- String deviceName;
-
+ NONE,
/**
- * The hardware version. For example, <vendor>/<model>/<revision>.
+ * Closed-loop active braking.
+ *
+ * This effect should produce a sharp, crisp end to the waveform
+ * Support is optional.
*/
- String hardwareVersion;
-
- /**
- * The sensor's serial number.
- */
- String serialNumber;
+ CLAB,
}
diff --git a/vibrator/aidl/android/hardware/vibrator/BrakingPwle.aidl b/vibrator/aidl/android/hardware/vibrator/BrakingPwle.aidl
new file mode 100644
index 0000000..00675ed
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/BrakingPwle.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.vibrator;
+
+import android.hardware.vibrator.Braking;
+
+/**
+ * BrakingPwle is defined as a segment of zero output acceleration amplitude of duration length.
+ *
+ * There should be no output acceleration and the vibrator should be off for the entire duration.
+ * If the hardware supports braking mechanism(s), they can be set here.
+ */
+@VintfStability
+parcelable BrakingPwle {
+ /**
+ * Braking mechanism applied to adjacent segments
+ */
+ Braking braking;
+ /**
+ * Total duration of zero output acceleration in the units of milliseconds.
+ */
+ int duration;
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index cba76dc..592d151 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -17,10 +17,12 @@
package android.hardware.vibrator;
import android.hardware.vibrator.IVibratorCallback;
+import android.hardware.vibrator.Braking;
import android.hardware.vibrator.Effect;
import android.hardware.vibrator.EffectStrength;
import android.hardware.vibrator.CompositeEffect;
import android.hardware.vibrator.CompositePrimitive;
+import android.hardware.vibrator.PrimitivePwle;
@VintfStability
interface IVibrator {
@@ -60,6 +62,14 @@
* Whether getQFactor is supported.
*/
const int CAP_GET_Q_FACTOR = 1 << 8;
+ /**
+ * Whether frequency control is supported.
+ */
+ const int CAP_FREQUENCY_CONTROL = 1 << 9;
+ /**
+ * Whether composePwle is supported.
+ */
+ const int CAP_COMPOSE_PWLE_EFFECTS = 1 << 10;
/**
* Determine capabilities of the vibrator HAL (CAP_* mask)
@@ -240,18 +250,109 @@
void alwaysOnDisable(in int id);
/**
- * Retrieve the measured resonant frequency of the actuator. This may not be supported
- * and this support is reflected in getCapabilities (CAP_GET_RESONANT_FREQUENCY)
+ * Retrieve the measured resonant frequency of the actuator.
*
- * @return Measured resonant frequency in Hz.
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_GET_RESONANT_FREQUENCY)
+ *
+ * @return Measured resonant frequency in Hz. Non-zero value if supported,
+ * or value should be ignored if not supported.
*/
float getResonantFrequency();
/**
- * Retrieve the measured Q factor. This may not be supported
- * and this support is reflected in getCapabilities (CAP_GET_Q_FACTOR)
+ * Retrieve the measured Q factor.
*
- * @return Measured Q factor.
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_GET_Q_FACTOR)
+ *
+ * @return Measured Q factor. Non-zero value if supported, or value should be
+ * ignored if not supported.
*/
float getQFactor();
+
+ /**
+ * Retrieve the frequency resolution used in getBandwidthAmplitudeMap() in units of hertz
+ *
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_FREQUENCY_CONTROL).
+ *
+ * @return The frequency resolution of the bandwidth amplitude map.
+ * Non-zero value if supported, or value should be ignored if not supported.
+ */
+ float getFrequencyResolution();
+
+ /**
+ * Retrieve the minimum allowed frequency in units of hertz
+ *
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_FREQUENCY_CONTROL).
+ *
+ * @return The minimum frequency allowed. Non-zero value if supported,
+ * or value should be ignored if not supported.
+ */
+ float getFrequencyMinimum();
+
+ /**
+ * Retrieve the output acceleration amplitude values per frequency supported
+ *
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_FREQUENCY_CONTROL).
+ *
+ * The mapping is represented as a list of amplitude values in the inclusive range [0.0, 1.0].
+ * The first value represents the amplitude at the frequency returned by getFrequencyMinimum().
+ * Each subsequent element is the amplitude at the next supported frequency, in increments
+ * of getFrequencyResolution(). The value returned by getResonantFrequency() must be
+ * represented in the returned list.
+ *
+ * @return The maximum output acceleration amplitude for each supported frequency,
+ * starting at getMinimumFrequency()
+ */
+ float[] getBandwidthAmplitudeMap();
+
+ /**
+ * Retrieve the maximum duration allowed for any primitive PWLE in units of milliseconds.
+ *
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_COMPOSE_PWLE_EFFECTS).
+ *
+ * @return The maximum duration allowed for a single PrimitivePwle.
+ * Non-zero value if supported, or value should be ignored if not supported.
+ */
+ int getPwlePrimitiveDurationMax();
+
+ /**
+ * Retrieve the maximum count for allowed PWLEs in one composition.
+ *
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_COMPOSE_PWLE_EFFECTS).
+ *
+ * @return The maximum count allowed. Non-zero value if supported,
+ * or value should be ignored if not supported.
+ */
+ int getPwleCompositionSizeMax();
+
+ /**
+ * List of supported braking mechanism.
+ *
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_COMPOSE_PWLE_EFFECTS).
+ * Implementations are optional but encouraged if available.
+ *
+ * @return The braking mechanisms which are supported by the composePwle API.
+ */
+ Braking[] getSupportedBraking();
+
+ /**
+ * Fire off a string of PWLEs.
+ *
+ * This may not be supported and this support is reflected in
+ * getCapabilities (CAP_COMPOSE_PWLE_EFFECTS).
+ *
+ * Doing this operation while the vibrator is already on is undefined behavior. Clients should
+ * explicitly call off. IVibratorCallback.onComplete() support is required for this API.
+ *
+ * @param composite Array of PWLEs.
+ */
+ void composePwle(in PrimitivePwle[] composite, in IVibratorCallback callback);
}
diff --git a/vibrator/aidl/android/hardware/vibrator/PrimitivePwle.aidl b/vibrator/aidl/android/hardware/vibrator/PrimitivePwle.aidl
new file mode 100644
index 0000000..813c7dc
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/PrimitivePwle.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.vibrator;
+
+import android.hardware.vibrator.ActivePwle;
+import android.hardware.vibrator.BrakingPwle;
+
+@VintfStability
+union PrimitivePwle {
+ ActivePwle active;
+ BrakingPwle braking;
+}
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index bf61bfe..c6682b3 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -26,9 +26,16 @@
static constexpr int32_t kComposeDelayMaxMs = 1000;
static constexpr int32_t kComposeSizeMax = 256;
+static constexpr int32_t kComposePwleSizeMax = 127;
static constexpr float kResonantFrequency = 150.0;
static constexpr float kQFactor = 11.0;
+static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
+static constexpr float PWLE_LEVEL_MIN = 0.0;
+static constexpr float PWLE_LEVEL_MAX = 0.98256;
+static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.0;
+static constexpr float PWLE_FREQUENCY_MIN_HZ = 140.0;
+static constexpr float PWLE_FREQUENCY_MAX_HZ = 160.0;
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
LOG(INFO) << "Vibrator reporting capabilities";
@@ -36,7 +43,8 @@
IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
- IVibrator::CAP_GET_Q_FACTOR;
+ IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
+ IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
return ndk::ScopedAStatus::ok();
}
@@ -215,6 +223,169 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
+ *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
+ *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
+ // A valid array should be of size:
+ // (PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ
+ *_aidl_return = {0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10,
+ 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20};
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
+ *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
+ *maxSize = kComposePwleSizeMax;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
+ *supported = {
+ Braking::NONE,
+ Braking::CLAB,
+ };
+ return ndk::ScopedAStatus::ok();
+}
+
+void resetPreviousEndAmplitudeEndFrequency(float &prevEndAmplitude, float &prevEndFrequency) {
+ const float reset = -1.0;
+ prevEndAmplitude = reset;
+ prevEndFrequency = reset;
+}
+
+void incrementIndex(int &index) {
+ index += 1;
+}
+
+void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) {
+ pwleBuilder << ",C" << segmentIdx << ":1";
+ pwleBuilder << ",B" << segmentIdx << ":0";
+ pwleBuilder << ",AR" << segmentIdx << ":0";
+ pwleBuilder << ",V" << segmentIdx << ":0";
+}
+
+void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
+ float amplitude, float frequency) {
+ pwleBuilder << ",T" << segmentIdx << ":" << duration;
+ pwleBuilder << ",L" << segmentIdx << ":" << amplitude;
+ pwleBuilder << ",F" << segmentIdx << ":" << frequency;
+ constructActiveDefaults(pwleBuilder, segmentIdx);
+}
+
+void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration,
+ Braking brakingType) {
+ pwleBuilder << ",T" << segmentIdx << ":" << duration;
+ pwleBuilder << ",L" << segmentIdx << ":" << 0;
+ pwleBuilder << ",F" << segmentIdx << ":" << 0;
+ pwleBuilder << ",C" << segmentIdx << ":0";
+ pwleBuilder << ",B" << segmentIdx << ":"
+ << static_cast<std::underlying_type<Braking>::type>(brakingType);
+ pwleBuilder << ",AR" << segmentIdx << ":0";
+ pwleBuilder << ",V" << segmentIdx << ":0";
+}
+
+ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
+ const std::shared_ptr<IVibratorCallback> &callback) {
+ std::ostringstream pwleBuilder;
+ std::string pwleQueue;
+
+ int compositionSizeMax;
+ getPwleCompositionSizeMax(&compositionSizeMax);
+ if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ float prevEndAmplitude;
+ float prevEndFrequency;
+ resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
+
+ int segmentIdx = 0;
+ uint32_t totalDuration = 0;
+
+ pwleBuilder << "S:0,WF:4,RP:0,WT:0";
+
+ for (auto &e : composite) {
+ switch (e.getTag()) {
+ case PrimitivePwle::active: {
+ auto active = e.get<PrimitivePwle::active>();
+ if (active.duration < 0 ||
+ active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (active.startAmplitude < PWLE_LEVEL_MIN ||
+ active.startAmplitude > PWLE_LEVEL_MAX ||
+ active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
+ active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
+ active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
+ active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (!((active.startAmplitude == prevEndAmplitude) &&
+ (active.startFrequency == prevEndFrequency))) {
+ constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude,
+ active.startFrequency);
+ incrementIndex(segmentIdx);
+ }
+
+ constructActiveSegment(pwleBuilder, segmentIdx, active.duration,
+ active.endAmplitude, active.endFrequency);
+ incrementIndex(segmentIdx);
+
+ prevEndAmplitude = active.endAmplitude;
+ prevEndFrequency = active.endFrequency;
+ totalDuration += active.duration;
+ break;
+ }
+ case PrimitivePwle::braking: {
+ auto braking = e.get<PrimitivePwle::braking>();
+ if (braking.braking > Braking::CLAB) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking);
+ incrementIndex(segmentIdx);
+
+ constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking);
+ incrementIndex(segmentIdx);
+
+ resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency);
+ totalDuration += braking.duration;
+ break;
+ }
+ }
+ }
+
+ std::thread([=] {
+ LOG(INFO) << "Starting composePwle on another thread";
+ usleep(totalDuration * 1000);
+ if (callback != nullptr) {
+ LOG(INFO) << "Notifying compose PWLE complete";
+ callback->onComplete();
+ }
+ }).detach();
+
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace vibrator
} // namespace hardware
} // namespace android
diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
index a2af963..4203bf2 100644
--- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
+++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
@@ -46,6 +46,15 @@
ndk::ScopedAStatus alwaysOnDisable(int32_t id) override;
ndk::ScopedAStatus getResonantFrequency(float *resonantFreqHz) override;
ndk::ScopedAStatus getQFactor(float *qFactor) override;
+ ndk::ScopedAStatus getFrequencyResolution(float *freqResolutionHz) override;
+ ndk::ScopedAStatus getFrequencyMinimum(float *freqMinimumHz) override;
+ ndk::ScopedAStatus getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) override;
+ ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t *durationMs) override;
+ ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t *maxSize) override;
+ ndk::ScopedAStatus getSupportedBraking(std::vector<Braking>* supported) override;
+ ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
+ const std::shared_ptr<IVibratorCallback> &callback) override;
+
};
} // namespace vibrator
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index 2540d0b..a9d1ed5 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -15,7 +15,6 @@
*/
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
-
#include <android/hardware/vibrator/BnVibratorCallback.h>
#include <android/hardware/vibrator/IVibrator.h>
#include <android/hardware/vibrator/IVibratorManager.h>
@@ -29,13 +28,17 @@
using android::sp;
using android::String16;
using android::binder::Status;
+using android::hardware::vibrator::ActivePwle;
using android::hardware::vibrator::BnVibratorCallback;
+using android::hardware::vibrator::Braking;
+using android::hardware::vibrator::BrakingPwle;
using android::hardware::vibrator::CompositeEffect;
using android::hardware::vibrator::CompositePrimitive;
using android::hardware::vibrator::Effect;
using android::hardware::vibrator::EffectStrength;
using android::hardware::vibrator::IVibrator;
using android::hardware::vibrator::IVibratorManager;
+using android::hardware::vibrator::PrimitivePwle;
using std::chrono::high_resolution_clock;
const std::vector<Effect> kEffects{android::enum_range<Effect>().begin(),
@@ -44,32 +47,32 @@
android::enum_range<EffectStrength>().end()};
const std::vector<Effect> kInvalidEffects = {
- static_cast<Effect>(static_cast<int32_t>(kEffects.front()) - 1),
- static_cast<Effect>(static_cast<int32_t>(kEffects.back()) + 1),
+ static_cast<Effect>(static_cast<int32_t>(kEffects.front()) - 1),
+ static_cast<Effect>(static_cast<int32_t>(kEffects.back()) + 1),
};
const std::vector<EffectStrength> kInvalidEffectStrengths = {
- static_cast<EffectStrength>(static_cast<int8_t>(kEffectStrengths.front()) - 1),
- static_cast<EffectStrength>(static_cast<int8_t>(kEffectStrengths.back()) + 1),
+ static_cast<EffectStrength>(static_cast<int8_t>(kEffectStrengths.front()) - 1),
+ static_cast<EffectStrength>(static_cast<int8_t>(kEffectStrengths.back()) + 1),
};
const std::vector<CompositePrimitive> kCompositePrimitives{
- android::enum_range<CompositePrimitive>().begin(),
- android::enum_range<CompositePrimitive>().end()};
+ android::enum_range<CompositePrimitive>().begin(),
+ android::enum_range<CompositePrimitive>().end()};
const std::vector<CompositePrimitive> kOptionalPrimitives = {
- CompositePrimitive::THUD,
- CompositePrimitive::SPIN,
+ CompositePrimitive::THUD,
+ CompositePrimitive::SPIN,
};
const std::vector<CompositePrimitive> kInvalidPrimitives = {
- static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.front()) - 1),
- static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.back()) + 1),
+ static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.front()) - 1),
+ static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.back()) + 1),
};
class CompletionCallback : public BnVibratorCallback {
public:
- CompletionCallback(const std::function<void()>& callback) : mCallback(callback) {}
+ CompletionCallback(const std::function<void()> &callback) : mCallback(callback) {}
Status onComplete() override {
mCallback();
return Status::ok();
@@ -109,6 +112,89 @@
int32_t capabilities;
};
+static float getResonantFrequencyHz(sp<IVibrator> vibrator, int32_t capabilities) {
+ float resonantFrequencyHz;
+ Status status = vibrator->getResonantFrequency(&resonantFrequencyHz);
+ if (capabilities & IVibrator::CAP_GET_RESONANT_FREQUENCY) {
+ EXPECT_GT(resonantFrequencyHz, 0);
+ EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+ return resonantFrequencyHz;
+}
+
+static float getFrequencyResolutionHz(sp<IVibrator> vibrator, int32_t capabilities) {
+ float freqResolutionHz;
+ Status status = vibrator->getFrequencyResolution(&freqResolutionHz);
+ if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+ EXPECT_GT(freqResolutionHz, 0);
+ EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+ return freqResolutionHz;
+}
+
+static float getFrequencyMinimumHz(sp<IVibrator> vibrator, int32_t capabilities) {
+ float freqMinimumHz;
+ Status status = vibrator->getFrequencyMinimum(&freqMinimumHz);
+ if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+
+ float resonantFrequencyHz = getResonantFrequencyHz(vibrator, capabilities);
+
+ EXPECT_GT(freqMinimumHz, 0);
+ EXPECT_LE(freqMinimumHz, resonantFrequencyHz);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+ return freqMinimumHz;
+}
+
+static float getFrequencyMaximumHz(sp<IVibrator> vibrator, int32_t capabilities) {
+ std::vector<float> bandwidthAmplitudeMap;
+ Status status = vibrator->getBandwidthAmplitudeMap(&bandwidthAmplitudeMap);
+ if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+
+ float freqMaximumHz =
+ (bandwidthAmplitudeMap.size() * getFrequencyResolutionHz(vibrator, capabilities)) +
+ getFrequencyMinimumHz(vibrator, capabilities);
+ return freqMaximumHz;
+}
+
+static float getAmplitudeMin() {
+ return 0.0;
+}
+
+static float getAmplitudeMax() {
+ return 1.0;
+}
+
+static ActivePwle composeValidActivePwle(sp<IVibrator> vibrator, int32_t capabilities) {
+ float frequencyHz;
+ if (capabilities & IVibrator::CAP_GET_RESONANT_FREQUENCY) {
+ frequencyHz = getResonantFrequencyHz(vibrator, capabilities);
+ } else if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+ frequencyHz = getFrequencyMinimumHz(vibrator, capabilities);
+ } else {
+ frequencyHz = 150.0; // default value commonly used
+ }
+
+ ActivePwle active;
+ active.startAmplitude = (getAmplitudeMin() + getAmplitudeMax()) / 2;
+ active.startFrequency = frequencyHz;
+ active.endAmplitude = (getAmplitudeMin() + getAmplitudeMax()) / 2;
+ active.endFrequency = frequencyHz;
+ active.duration = 1000;
+
+ return active;
+}
+
TEST_P(VibratorAidl, OnThenOffBeforeTimeout) {
EXPECT_TRUE(vibrator->on(2000, nullptr /*callback*/).isOk());
sleep(1);
@@ -116,12 +202,13 @@
}
TEST_P(VibratorAidl, OnWithCallback) {
- if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) return;
+ if (!(capabilities & IVibrator::CAP_ON_CALLBACK))
+ return;
std::promise<void> completionPromise;
std::future<void> completionFuture{completionPromise.get_future()};
sp<CompletionCallback> callback =
- new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+ new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
uint32_t durationMs = 250;
std::chrono::milliseconds timeout{durationMs * 2};
EXPECT_TRUE(vibrator->on(durationMs, callback).isOk());
@@ -142,7 +229,7 @@
for (Effect effect : kEffects) {
bool isEffectSupported =
- std::find(supported.begin(), supported.end(), effect) != supported.end();
+ std::find(supported.begin(), supported.end(), effect) != supported.end();
for (EffectStrength strength : kEffectStrengths) {
int32_t lengthMs = 0;
@@ -154,27 +241,28 @@
usleep(lengthMs * 1000);
} else {
EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION)
- << toString(effect) << " " << toString(strength);
+ << toString(effect) << " " << toString(strength);
}
}
}
}
TEST_P(VibratorAidl, ValidateEffectWithCallback) {
- if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) return;
+ if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK))
+ return;
std::vector<Effect> supported;
ASSERT_TRUE(vibrator->getSupportedEffects(&supported).isOk());
for (Effect effect : kEffects) {
bool isEffectSupported =
- std::find(supported.begin(), supported.end(), effect) != supported.end();
+ std::find(supported.begin(), supported.end(), effect) != supported.end();
for (EffectStrength strength : kEffectStrengths) {
std::promise<void> completionPromise;
std::future<void> completionFuture{completionPromise.get_future()};
sp<CompletionCallback> callback =
- new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+ new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
int lengthMs = 0;
Status status = vibrator->perform(effect, strength, callback, &lengthMs);
@@ -185,7 +273,8 @@
EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
}
- if (!status.isOk()) continue;
+ if (!status.isOk())
+ continue;
std::chrono::milliseconds timeout{lengthMs * 2};
EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
@@ -194,7 +283,8 @@
}
TEST_P(VibratorAidl, ValidateEffectWithCallbackNotSupported) {
- if (capabilities & IVibrator::CAP_PERFORM_CALLBACK) return;
+ if (capabilities & IVibrator::CAP_PERFORM_CALLBACK)
+ return;
for (Effect effect : kEffects) {
for (EffectStrength strength : kEffectStrengths) {
@@ -212,7 +302,7 @@
int32_t lengthMs;
Status status = vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION)
- << toString(effect) << " " << toString(strength);
+ << toString(effect) << " " << toString(strength);
}
}
for (Effect effect : kEffects) {
@@ -220,7 +310,7 @@
int32_t lengthMs;
Status status = vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION)
- << toString(effect) << " " << toString(strength);
+ << toString(effect) << " " << toString(strength);
}
}
}
@@ -261,7 +351,7 @@
TEST_P(VibratorAidl, ExternalAmplitudeControl) {
const bool supportsExternalAmplitudeControl =
- (capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL) > 0;
+ (capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL) > 0;
if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) {
EXPECT_TRUE(vibrator->setExternalControl(true).isOk());
@@ -293,10 +383,10 @@
for (auto primitive : kCompositePrimitives) {
bool isPrimitiveSupported =
- std::find(supported.begin(), supported.end(), primitive) != supported.end();
+ std::find(supported.begin(), supported.end(), primitive) != supported.end();
bool isPrimitiveOptional =
- std::find(kOptionalPrimitives.begin(), kOptionalPrimitives.end(), primitive) !=
- kOptionalPrimitives.end();
+ std::find(kOptionalPrimitives.begin(), kOptionalPrimitives.end(), primitive) !=
+ kOptionalPrimitives.end();
EXPECT_TRUE(isPrimitiveSupported || isPrimitiveOptional) << toString(primitive);
}
@@ -310,7 +400,7 @@
for (auto primitive : kCompositePrimitives) {
bool isPrimitiveSupported =
- std::find(supported.begin(), supported.end(), primitive) != supported.end();
+ std::find(supported.begin(), supported.end(), primitive) != supported.end();
int32_t duration;
Status status = vibrator->getPrimitiveDuration(primitive, &duration);
@@ -366,7 +456,7 @@
for (auto primitive : kCompositePrimitives) {
bool isPrimitiveSupported =
- std::find(supported.begin(), supported.end(), primitive) != supported.end();
+ std::find(supported.begin(), supported.end(), primitive) != supported.end();
if (!isPrimitiveSupported) {
unsupported.push_back(primitive);
@@ -376,7 +466,7 @@
for (auto primitive : unsupported) {
std::vector<CompositeEffect> composite(1);
- for (auto& effect : composite) {
+ for (auto &effect : composite) {
effect.delayMs = 0;
effect.primitive = primitive;
effect.scale = 1.0f;
@@ -391,7 +481,7 @@
TEST_P(VibratorAidl, ComposeScaleBoundary) {
if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
std::vector<CompositeEffect> composite(1);
- CompositeEffect& effect = composite[0];
+ CompositeEffect &effect = composite[0];
effect.delayMs = 0;
effect.primitive = CompositePrimitive::CLICK;
@@ -478,7 +568,7 @@
std::promise<void> completionPromise;
std::future<void> completionFuture{completionPromise.get_future()};
sp<CompletionCallback> callback =
- new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+ new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
CompositeEffect effect;
std::vector<CompositeEffect> composite;
int32_t durationMs;
@@ -493,16 +583,15 @@
EXPECT_EQ(Status::EX_NONE,
vibrator->getPrimitiveDuration(primitive, &durationMs).exceptionCode())
- << toString(primitive);
+ << toString(primitive);
duration = std::chrono::milliseconds(durationMs);
EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode())
- << toString(primitive);
+ << toString(primitive);
start = high_resolution_clock::now();
- EXPECT_EQ(completionFuture.wait_for(duration + allowedLatency),
- std::future_status::ready)
- << toString(primitive);
+ EXPECT_EQ(completionFuture.wait_for(duration + allowedLatency), std::future_status::ready)
+ << toString(primitive);
end = high_resolution_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
@@ -519,17 +608,17 @@
for (Effect effect : kEffects) {
bool isEffectSupported =
- std::find(supported.begin(), supported.end(), effect) != supported.end();
+ std::find(supported.begin(), supported.end(), effect) != supported.end();
for (EffectStrength strength : kEffectStrengths) {
Status status = vibrator->alwaysOnEnable(0, effect, strength);
if (isEffectSupported) {
EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
- << toString(effect) << " " << toString(strength);
+ << toString(effect) << " " << toString(strength);
} else {
EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, status.exceptionCode())
- << toString(effect) << " " << toString(strength);
+ << toString(effect) << " " << toString(strength);
}
}
}
@@ -539,27 +628,253 @@
}
TEST_P(VibratorAidl, GetResonantFrequency) {
- float resonantFrequency;
- Status status = vibrator->getResonantFrequency(&resonantFrequency);
- if (capabilities & IVibrator::CAP_GET_RESONANT_FREQUENCY) {
- ASSERT_NE(resonantFrequency, 0);
- EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
- } else {
- EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
- }
+ getResonantFrequencyHz(vibrator, capabilities);
}
TEST_P(VibratorAidl, GetQFactor) {
float qFactor;
Status status = vibrator->getQFactor(&qFactor);
if (capabilities & IVibrator::CAP_GET_Q_FACTOR) {
- ASSERT_NE(qFactor, 0);
+ ASSERT_GT(qFactor, 0);
EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
} else {
EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
}
}
+TEST_P(VibratorAidl, GetFrequencyResolution) {
+ getFrequencyResolutionHz(vibrator, capabilities);
+}
+
+TEST_P(VibratorAidl, GetFrequencyMinimum) {
+ getFrequencyMinimumHz(vibrator, capabilities);
+}
+
+TEST_P(VibratorAidl, GetBandwidthAmplitudeMap) {
+ std::vector<float> bandwidthAmplitudeMap;
+ Status status = vibrator->getBandwidthAmplitudeMap(&bandwidthAmplitudeMap);
+ if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+ ASSERT_FALSE(bandwidthAmplitudeMap.empty());
+
+ int minMapSize = (getResonantFrequencyHz(vibrator, capabilities) -
+ getFrequencyMinimumHz(vibrator, capabilities)) /
+ getFrequencyResolutionHz(vibrator, capabilities);
+ ASSERT_GT(bandwidthAmplitudeMap.size(), minMapSize);
+
+ for (float e : bandwidthAmplitudeMap) {
+ ASSERT_GE(e, 0.0);
+ ASSERT_LE(e, 1.0);
+ }
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+TEST_P(VibratorAidl, GetPwlePrimitiveDurationMax) {
+ int32_t durationMs;
+ Status status = vibrator->getPwlePrimitiveDurationMax(&durationMs);
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ ASSERT_NE(durationMs, 0);
+ EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+TEST_P(VibratorAidl, GetPwleCompositionSizeMax) {
+ int32_t maxSize;
+ Status status = vibrator->getPwleCompositionSizeMax(&maxSize);
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ ASSERT_NE(maxSize, 0);
+ EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+TEST_P(VibratorAidl, GetSupportedBraking) {
+ std::vector<Braking> supported;
+ Status status = vibrator->getSupportedBraking(&supported);
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ bool isDefaultNoneSupported =
+ std::find(supported.begin(), supported.end(), Braking::NONE) != supported.end();
+ ASSERT_TRUE(isDefaultNoneSupported);
+ EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+TEST_P(VibratorAidl, ComposeValidPwle) {
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ ActivePwle active = composeValidActivePwle(vibrator, capabilities);
+
+ std::vector<Braking> supported;
+ ASSERT_TRUE(vibrator->getSupportedBraking(&supported).isOk());
+ bool isClabSupported =
+ std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end();
+ BrakingPwle braking;
+ braking.braking = isClabSupported ? Braking::CLAB : Braking::NONE;
+ braking.duration = 100;
+
+ std::vector<PrimitivePwle> pwleQueue;
+ PrimitivePwle pwle;
+ pwle = active;
+ pwleQueue.emplace_back(std::move(pwle));
+ pwle = braking;
+ pwleQueue.emplace_back(std::move(pwle));
+ pwle = active;
+ pwleQueue.emplace_back(std::move(pwle));
+
+ EXPECT_EQ(Status::EX_NONE, vibrator->composePwle(pwleQueue, nullptr).exceptionCode());
+ vibrator->off();
+ }
+}
+
+TEST_P(VibratorAidl, ComposeValidPwleWithCallback) {
+ if (!((capabilities & IVibrator::CAP_ON_CALLBACK) &&
+ (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS)))
+ return;
+
+ std::promise<void> completionPromise;
+ std::future<void> completionFuture{completionPromise.get_future()};
+ sp<CompletionCallback> callback =
+ new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+ uint32_t durationMs = 2100; // Sum of 2 active and 1 braking below
+ std::chrono::milliseconds timeout{durationMs * 2};
+
+ ActivePwle active = composeValidActivePwle(vibrator, capabilities);
+
+ std::vector<Braking> supported;
+ ASSERT_TRUE(vibrator->getSupportedBraking(&supported).isOk());
+ bool isClabSupported =
+ std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end();
+ BrakingPwle braking;
+ braking.braking = isClabSupported ? Braking::CLAB : Braking::NONE;
+ braking.duration = 100;
+
+ std::vector<PrimitivePwle> pwleQueue;
+ PrimitivePwle pwle;
+ pwle = active;
+ pwleQueue.emplace_back(std::move(pwle));
+ pwle = braking;
+ pwleQueue.emplace_back(std::move(pwle));
+ pwle = active;
+ pwleQueue.emplace_back(std::move(pwle));
+
+ EXPECT_TRUE(vibrator->composePwle(pwleQueue, callback).isOk());
+ EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
+ EXPECT_TRUE(vibrator->off().isOk());
+}
+
+TEST_P(VibratorAidl, ComposePwleSegmentBoundary) {
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ std::vector<PrimitivePwle> pwleQueue;
+ // test empty queue
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->composePwle(pwleQueue, nullptr).exceptionCode());
+ vibrator->off();
+
+ ActivePwle active = composeValidActivePwle(vibrator, capabilities);
+
+ PrimitivePwle pwle;
+ pwle = active;
+ int segmentCountMax;
+ vibrator->getPwleCompositionSizeMax(&segmentCountMax);
+
+ // Create PWLE queue with more segments than allowed
+ for (int i = 0; i < segmentCountMax + 10; i++) {
+ pwleQueue.emplace_back(std::move(pwle));
+ }
+
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->composePwle(pwleQueue, nullptr).exceptionCode());
+ vibrator->off();
+ }
+}
+
+TEST_P(VibratorAidl, ComposePwleAmplitudeParameterBoundary) {
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ ActivePwle active = composeValidActivePwle(vibrator, capabilities);
+ active.startAmplitude = getAmplitudeMax() + 1.0; // Amplitude greater than allowed
+ active.endAmplitude = getAmplitudeMax() + 1.0; // Amplitude greater than allowed
+
+ std::vector<PrimitivePwle> pwleQueueGreater;
+ PrimitivePwle pwle;
+ pwle = active;
+ pwleQueueGreater.emplace_back(std::move(pwle));
+
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->composePwle(pwleQueueGreater, nullptr).exceptionCode());
+ vibrator->off();
+
+ active.startAmplitude = getAmplitudeMin() - 1.0; // Amplitude less than allowed
+ active.endAmplitude = getAmplitudeMin() - 1.0; // Amplitude less than allowed
+
+ std::vector<PrimitivePwle> pwleQueueLess;
+ pwle = active;
+ pwleQueueLess.emplace_back(std::move(pwle));
+
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->composePwle(pwleQueueLess, nullptr).exceptionCode());
+ vibrator->off();
+ }
+}
+
+TEST_P(VibratorAidl, ComposePwleFrequencyParameterBoundary) {
+ if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) &&
+ (capabilities & IVibrator::CAP_FREQUENCY_CONTROL)) {
+ float freqMinimumHz = getFrequencyMinimumHz(vibrator, capabilities);
+ float freqMaximumHz = getFrequencyMaximumHz(vibrator, capabilities);
+ float freqResolutionHz = getFrequencyResolutionHz(vibrator, capabilities);
+
+ ActivePwle active = composeValidActivePwle(vibrator, capabilities);
+ active.startFrequency =
+ freqMaximumHz + freqResolutionHz; // Frequency greater than allowed
+ active.endFrequency = freqMaximumHz + freqResolutionHz; // Frequency greater than allowed
+
+ std::vector<PrimitivePwle> pwleQueueGreater;
+ PrimitivePwle pwle;
+ pwle = active;
+ pwleQueueGreater.emplace_back(std::move(pwle));
+
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->composePwle(pwleQueueGreater, nullptr).exceptionCode());
+ vibrator->off();
+
+ active.startFrequency = freqMinimumHz - freqResolutionHz; // Frequency less than allowed
+ active.endFrequency = freqMinimumHz - freqResolutionHz; // Frequency less than allowed
+
+ std::vector<PrimitivePwle> pwleQueueLess;
+ pwle = active;
+ pwleQueueLess.emplace_back(std::move(pwle));
+
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->composePwle(pwleQueueLess, nullptr).exceptionCode());
+ vibrator->off();
+ }
+}
+
+TEST_P(VibratorAidl, ComposePwleSegmentDurationBoundary) {
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ ActivePwle active = composeValidActivePwle(vibrator, capabilities);
+
+ int segmentDurationMaxMs;
+ vibrator->getPwlePrimitiveDurationMax(&segmentDurationMaxMs);
+ active.duration = segmentDurationMaxMs + 10; // Segment duration greater than allowed
+
+ std::vector<PrimitivePwle> pwleQueue;
+ PrimitivePwle pwle;
+ pwle = active;
+ pwleQueue.emplace_back(std::move(pwle));
+
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->composePwle(pwleQueue, nullptr).exceptionCode());
+ vibrator->off();
+ }
+}
+
std::vector<std::tuple<int32_t, int32_t>> GenerateVibratorMapping() {
std::vector<std::tuple<int32_t, int32_t>> tuples;
auto managerAidlNames = android::getAidlHalInstanceNames(IVibratorManager::descriptor);
@@ -569,7 +884,7 @@
auto managerName = String16(managerAidlNames[i].c_str());
auto vibratorManager = android::waitForDeclaredService<IVibratorManager>(managerName);
if (vibratorManager->getVibratorIds(&vibratorIds).isOk()) {
- for (auto& vibratorId : vibratorIds) {
+ for (auto &vibratorId : vibratorIds) {
tuples.push_back(std::make_tuple(i, vibratorId));
}
}
@@ -583,8 +898,8 @@
return tuples;
}
-std::string PrintGeneratedTest(const testing::TestParamInfo<VibratorAidl::ParamType>& info) {
- const auto& [managerIdx, vibratorId] = info.param;
+std::string PrintGeneratedTest(const testing::TestParamInfo<VibratorAidl::ParamType> &info) {
+ const auto &[managerIdx, vibratorId] = info.param;
if (managerIdx < 0) {
return std::string("TOP_LEVEL_VIBRATOR_") + std::to_string(vibratorId);
}
@@ -596,7 +911,7 @@
INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl, testing::ValuesIn(GenerateVibratorMapping()),
PrintGeneratedTest);
-int main(int argc, char** argv) {
+int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
ProcessState::self()->setThreadPoolMaxThreadCount(1);
ProcessState::self()->startThreadPool();
diff --git a/wifi/1.5/Android.bp b/wifi/1.5/Android.bp
index 7c04c69..0887c6b 100644
--- a/wifi/1.5/Android.bp
+++ b/wifi/1.5/Android.bp
@@ -20,6 +20,7 @@
"IWifiNanIface.hal",
"IWifiNanIfaceEventCallback.hal",
"IWifiStaIface.hal",
+ "IWifiEventCallback.hal",
],
interfaces: [
"android.hardware.wifi@1.0",
diff --git a/wifi/1.5/IWifi.hal b/wifi/1.5/IWifi.hal
index 66d0a9c..28b808e 100644
--- a/wifi/1.5/IWifi.hal
+++ b/wifi/1.5/IWifi.hal
@@ -17,6 +17,8 @@
package android.hardware.wifi@1.5;
import @1.4::IWifi;
+import IWifiEventCallback;
+import @1.0::WifiStatus;
/**
* This is the root of the HAL module and is the interface returned when
@@ -24,4 +26,21 @@
* module loaded in the system.
* IWifi.getChip() must return @1.5::IWifiChip
*/
-interface IWifi extends @1.4::IWifi {};
+interface IWifi extends @1.4::IWifi {
+ /**
+ * Requests notifications of significant events for the HAL. Multiple calls to
+ * this must register multiple callbacks each of which must receive all
+ * events. |IWifiEventCallback| object registration must be independent of the
+ * state of the rest of the HAL and must persist though stops/starts. These
+ * objects must be deleted when the corresponding client process is dead.
+ *
+ * @param callback An instance of the |IWifiEventCallback| HIDL interface
+ * object.
+ * @return status WifiStatus of the operation.
+ * Possible status codes:
+ * |WifiStatusCode.SUCCESS|,
+ * |WifiStatusCode.UNKNOWN|
+ */
+ registerEventCallback_1_5(IWifiEventCallback callback)
+ generates (WifiStatus status);
+};
diff --git a/wifi/1.5/IWifiChip.hal b/wifi/1.5/IWifiChip.hal
index 5a3e288..e199850 100644
--- a/wifi/1.5/IWifiChip.hal
+++ b/wifi/1.5/IWifiChip.hal
@@ -303,4 +303,25 @@
getUsableChannels(WifiBand band, bitfield<WifiIfaceMode> ifaceModeMask,
bitfield<UsableChannelFilter> filterMask)
generates (WifiStatus status, vec<WifiUsableChannel> channels);
+
+ /**
+ * Trigger subsystem restart
+ *
+ * If the framework detects a problem (e.g. connection failure),
+ * it must call this function to attempt recovery.
+ *
+ * When the wifi HAL receiveds triggerSubsystemRestart(), it must restart
+ * the wlan subsystem, especially the wlan firmware.
+ *
+ * Regarding the callback function for subsystem restart, refer to documentation of
+ * |IWifiEventCallback.onSubsystemRestart| for details.
+ *
+ * @return status WifiStatus of the operation.
+ * Possible status codes:
+ * |WifiStatusCode.SUCCESS|,
+ * |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+ * |WifiStatusCode.ERROR_NOT_AVAILABLE|,
+ * |WifiStatusCode.ERROR_UNKNOWN|
+ */
+ triggerSubsystemRestart() generates (WifiStatus status);
};
diff --git a/wifi/1.5/IWifiEventCallback.hal b/wifi/1.5/IWifiEventCallback.hal
new file mode 100644
index 0000000..ff27630
--- /dev/null
+++ b/wifi/1.5/IWifiEventCallback.hal
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.hardware.wifi@1.5;
+
+import @1.0::IWifiEventCallback;
+import @1.0::WifiStatus;
+
+interface IWifiEventCallback extends @1.0::IWifiEventCallback {
+ /**
+ * Must be called when the Wi-Fi subsystem restart completes.
+ * Once this event is received, framework must fully reset the Wi-Fi stack state.
+ */
+ oneway onSubsystemRestart(WifiStatus status);
+};
diff --git a/wifi/1.5/default/hidl_struct_util.cpp b/wifi/1.5/default/hidl_struct_util.cpp
index cd0edbe..338a8f1 100644
--- a/wifi/1.5/default/hidl_struct_util.cpp
+++ b/wifi/1.5/default/hidl_struct_util.cpp
@@ -954,27 +954,29 @@
bool convertLegacyLinkLayerRadioStatsToHidl(
const legacy_hal::LinkLayerRadioStats& legacy_radio_stat,
- V1_3::StaLinkLayerRadioStats* hidl_radio_stat) {
+ V1_5::StaLinkLayerRadioStats* hidl_radio_stat) {
if (!hidl_radio_stat) {
return false;
}
*hidl_radio_stat = {};
- hidl_radio_stat->V1_0.onTimeInMs = legacy_radio_stat.stats.on_time;
- hidl_radio_stat->V1_0.txTimeInMs = legacy_radio_stat.stats.tx_time;
- hidl_radio_stat->V1_0.rxTimeInMs = legacy_radio_stat.stats.rx_time;
- hidl_radio_stat->V1_0.onTimeInMsForScan =
+ hidl_radio_stat->radioId = legacy_radio_stat.stats.radio;
+ hidl_radio_stat->V1_3.V1_0.onTimeInMs = legacy_radio_stat.stats.on_time;
+ hidl_radio_stat->V1_3.V1_0.txTimeInMs = legacy_radio_stat.stats.tx_time;
+ hidl_radio_stat->V1_3.V1_0.rxTimeInMs = legacy_radio_stat.stats.rx_time;
+ hidl_radio_stat->V1_3.V1_0.onTimeInMsForScan =
legacy_radio_stat.stats.on_time_scan;
- hidl_radio_stat->V1_0.txTimeInMsPerLevel =
+ hidl_radio_stat->V1_3.V1_0.txTimeInMsPerLevel =
legacy_radio_stat.tx_time_per_levels;
- hidl_radio_stat->onTimeInMsForNanScan = legacy_radio_stat.stats.on_time_nbd;
- hidl_radio_stat->onTimeInMsForBgScan =
+ hidl_radio_stat->V1_3.onTimeInMsForNanScan =
+ legacy_radio_stat.stats.on_time_nbd;
+ hidl_radio_stat->V1_3.onTimeInMsForBgScan =
legacy_radio_stat.stats.on_time_gscan;
- hidl_radio_stat->onTimeInMsForRoamScan =
+ hidl_radio_stat->V1_3.onTimeInMsForRoamScan =
legacy_radio_stat.stats.on_time_roam_scan;
- hidl_radio_stat->onTimeInMsForPnoScan =
+ hidl_radio_stat->V1_3.onTimeInMsForPnoScan =
legacy_radio_stat.stats.on_time_pno_scan;
- hidl_radio_stat->onTimeInMsForHs20Scan =
+ hidl_radio_stat->V1_3.onTimeInMsForHs20Scan =
legacy_radio_stat.stats.on_time_hs20;
std::vector<V1_3::WifiChannelStats> hidl_channel_stats;
@@ -996,7 +998,7 @@
hidl_channel_stats.push_back(hidl_channel_stat);
}
- hidl_radio_stat->channelStats = hidl_channel_stats;
+ hidl_radio_stat->V1_3.channelStats = hidl_channel_stats;
return true;
}
@@ -1077,10 +1079,21 @@
legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_num_samples;
hidl_stats->iface.timeSliceDutyCycleInPercent =
legacy_stats.iface.info.time_slicing_duty_cycle_percent;
+ // peer info legacy_stats conversion.
+ std::vector<StaPeerInfo> hidl_peers_info_stats;
+ for (const auto& legacy_peer_info_stats : legacy_stats.peers) {
+ StaPeerInfo hidl_peer_info_stats;
+ if (!convertLegacyPeerInfoStatsToHidl(legacy_peer_info_stats,
+ &hidl_peer_info_stats)) {
+ return false;
+ }
+ hidl_peers_info_stats.push_back(hidl_peer_info_stats);
+ }
+ hidl_stats->iface.peers = hidl_peers_info_stats;
// radio legacy_stats conversion.
- std::vector<V1_3::StaLinkLayerRadioStats> hidl_radios_stats;
+ std::vector<V1_5::StaLinkLayerRadioStats> hidl_radios_stats;
for (const auto& legacy_radio_stats : legacy_stats.radios) {
- V1_3::StaLinkLayerRadioStats hidl_radio_stats;
+ V1_5::StaLinkLayerRadioStats hidl_radio_stats;
if (!convertLegacyLinkLayerRadioStatsToHidl(legacy_radio_stats,
&hidl_radio_stats)) {
return false;
@@ -1094,6 +1107,35 @@
return true;
}
+bool convertLegacyPeerInfoStatsToHidl(
+ const legacy_hal::WifiPeerInfo& legacy_peer_info_stats,
+ StaPeerInfo* hidl_peer_info_stats) {
+ if (!hidl_peer_info_stats) {
+ return false;
+ }
+ *hidl_peer_info_stats = {};
+ hidl_peer_info_stats->staCount =
+ legacy_peer_info_stats.peer_info.bssload.sta_count;
+ hidl_peer_info_stats->chanUtil =
+ legacy_peer_info_stats.peer_info.bssload.chan_util;
+
+ std::vector<StaRateStat> hidlRateStats;
+ for (const auto& legacy_rate_stats : legacy_peer_info_stats.rate_stats) {
+ StaRateStat rateStat;
+ if (!convertLegacyWifiRateInfoToHidl(legacy_rate_stats.rate,
+ &rateStat.rateInfo)) {
+ return false;
+ }
+ rateStat.txMpdu = legacy_rate_stats.tx_mpdu;
+ rateStat.rxMpdu = legacy_rate_stats.rx_mpdu;
+ rateStat.mpduLost = legacy_rate_stats.mpdu_lost;
+ rateStat.retries = legacy_rate_stats.retries;
+ hidlRateStats.push_back(rateStat);
+ }
+ hidl_peer_info_stats->rateStats = hidlRateStats;
+ return true;
+}
+
bool convertLegacyRoamingCapabilitiesToHidl(
const legacy_hal::wifi_roaming_capabilities& legacy_caps,
StaRoamingCapabilities* hidl_caps) {
@@ -2517,10 +2559,9 @@
return WifiChannelWidthInMhz::WIDTH_5;
case legacy_hal::WIFI_CHAN_WIDTH_10:
return WifiChannelWidthInMhz::WIDTH_10;
- case legacy_hal::WIFI_CHAN_WIDTH_INVALID:
+ default:
return WifiChannelWidthInMhz::WIDTH_INVALID;
};
- CHECK(false) << "Unknown legacy type: " << type;
}
legacy_hal::wifi_rtt_preamble convertHidlRttPreambleToLegacy(
diff --git a/wifi/1.5/default/hidl_struct_util.h b/wifi/1.5/default/hidl_struct_util.h
index 8b81033..352f213 100644
--- a/wifi/1.5/default/hidl_struct_util.h
+++ b/wifi/1.5/default/hidl_struct_util.h
@@ -212,6 +212,11 @@
bool convertLegacyWifiUsableChannelsToHidl(
const std::vector<legacy_hal::wifi_usable_channel>& legacy_usable_channels,
std::vector<V1_5::WifiUsableChannel>* hidl_usable_channels);
+bool convertLegacyPeerInfoStatsToHidl(
+ const legacy_hal::WifiPeerInfo& legacy_peer_info_stats,
+ StaPeerInfo* hidl_peer_info_stats);
+bool convertLegacyWifiRateInfoToHidl(const legacy_hal::wifi_rate& legacy_rate,
+ V1_4::WifiRateInfo* hidl_rate);
} // namespace hidl_struct_util
} // namespace implementation
} // namespace V1_5
diff --git a/wifi/1.5/default/service.cpp b/wifi/1.5/default/service.cpp
index 23e2b47..3de49b2 100644
--- a/wifi/1.5/default/service.cpp
+++ b/wifi/1.5/default/service.cpp
@@ -32,7 +32,6 @@
using android::hardware::LazyServiceRegistrar;
using android::hardware::wifi::V1_5::implementation::feature_flags::
WifiFeatureFlags;
-using android::hardware::wifi::V1_5::implementation::iface_util::WifiIfaceUtil;
using android::hardware::wifi::V1_5::implementation::legacy_hal::WifiLegacyHal;
using android::hardware::wifi::V1_5::implementation::legacy_hal::
WifiLegacyHalFactory;
@@ -63,7 +62,6 @@
new android::hardware::wifi::V1_5::implementation::Wifi(
iface_tool, legacy_hal_factory,
std::make_shared<WifiModeController>(),
- std::make_shared<WifiIfaceUtil>(iface_tool),
std::make_shared<WifiFeatureFlags>());
if (kLazyService) {
auto registrar = LazyServiceRegistrar::getInstance();
diff --git a/wifi/1.5/default/tests/hidl_struct_util_unit_tests.cpp b/wifi/1.5/default/tests/hidl_struct_util_unit_tests.cpp
index 6391a6a..4b4ebd6 100644
--- a/wifi/1.5/default/tests/hidl_struct_util_unit_tests.cpp
+++ b/wifi/1.5/default/tests/hidl_struct_util_unit_tests.cpp
@@ -132,6 +132,8 @@
legacy_hal::LinkLayerStats legacy_stats{};
legacy_stats.radios.push_back(legacy_hal::LinkLayerRadioStats{});
legacy_stats.radios.push_back(legacy_hal::LinkLayerRadioStats{});
+ legacy_stats.peers.push_back(legacy_hal::WifiPeerInfo{});
+ legacy_stats.peers.push_back(legacy_hal::WifiPeerInfo{});
legacy_stats.iface.beacon_rx = rand();
legacy_stats.iface.rssi_mgmt = rand();
legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu = rand();
@@ -175,8 +177,10 @@
rand();
legacy_stats.iface.info.time_slicing_duty_cycle_percent = rand();
+ legacy_stats.iface.num_peers = 1;
for (auto& radio : legacy_stats.radios) {
+ radio.stats.radio = rand();
radio.stats.on_time = rand();
radio.stats.tx_time = rand();
radio.stats.rx_time = rand();
@@ -204,6 +208,31 @@
radio.channel_stats.push_back(channel_stat2);
}
+ for (auto& peer : legacy_stats.peers) {
+ peer.peer_info.bssload.sta_count = rand();
+ peer.peer_info.bssload.chan_util = rand();
+ wifi_rate_stat rate_stat1 = {
+ .rate = {3, 1, 2, 5, 0, 0},
+ .tx_mpdu = 0,
+ .rx_mpdu = 1,
+ .mpdu_lost = 2,
+ .retries = 3,
+ .retries_short = 4,
+ .retries_long = 5,
+ };
+ wifi_rate_stat rate_stat2 = {
+ .rate = {2, 2, 1, 6, 0, 1},
+ .tx_mpdu = 6,
+ .rx_mpdu = 7,
+ .mpdu_lost = 8,
+ .retries = 9,
+ .retries_short = 10,
+ .retries_long = 11,
+ };
+ peer.rate_stats.push_back(rate_stat1);
+ peer.rate_stats.push_back(rate_stat2);
+ }
+
V1_5::StaLinkLayerStats converted{};
hidl_struct_util::convertLegacyLinkLayerStatsToHidl(legacy_stats,
&converted);
@@ -286,48 +315,84 @@
EXPECT_EQ(legacy_stats.radios.size(), converted.radios.size());
for (size_t i = 0; i < legacy_stats.radios.size(); i++) {
+ EXPECT_EQ(legacy_stats.radios[i].stats.radio,
+ converted.radios[i].radioId);
EXPECT_EQ(legacy_stats.radios[i].stats.on_time,
- converted.radios[i].V1_0.onTimeInMs);
+ converted.radios[i].V1_3.V1_0.onTimeInMs);
EXPECT_EQ(legacy_stats.radios[i].stats.tx_time,
- converted.radios[i].V1_0.txTimeInMs);
+ converted.radios[i].V1_3.V1_0.txTimeInMs);
EXPECT_EQ(legacy_stats.radios[i].stats.rx_time,
- converted.radios[i].V1_0.rxTimeInMs);
+ converted.radios[i].V1_3.V1_0.rxTimeInMs);
EXPECT_EQ(legacy_stats.radios[i].stats.on_time_scan,
- converted.radios[i].V1_0.onTimeInMsForScan);
+ converted.radios[i].V1_3.V1_0.onTimeInMsForScan);
EXPECT_EQ(legacy_stats.radios[i].tx_time_per_levels.size(),
- converted.radios[i].V1_0.txTimeInMsPerLevel.size());
+ converted.radios[i].V1_3.V1_0.txTimeInMsPerLevel.size());
for (size_t j = 0; j < legacy_stats.radios[i].tx_time_per_levels.size();
j++) {
EXPECT_EQ(legacy_stats.radios[i].tx_time_per_levels[j],
- converted.radios[i].V1_0.txTimeInMsPerLevel[j]);
+ converted.radios[i].V1_3.V1_0.txTimeInMsPerLevel[j]);
}
EXPECT_EQ(legacy_stats.radios[i].stats.on_time_nbd,
- converted.radios[i].onTimeInMsForNanScan);
+ converted.radios[i].V1_3.onTimeInMsForNanScan);
EXPECT_EQ(legacy_stats.radios[i].stats.on_time_gscan,
- converted.radios[i].onTimeInMsForBgScan);
+ converted.radios[i].V1_3.onTimeInMsForBgScan);
EXPECT_EQ(legacy_stats.radios[i].stats.on_time_roam_scan,
- converted.radios[i].onTimeInMsForRoamScan);
+ converted.radios[i].V1_3.onTimeInMsForRoamScan);
EXPECT_EQ(legacy_stats.radios[i].stats.on_time_pno_scan,
- converted.radios[i].onTimeInMsForPnoScan);
+ converted.radios[i].V1_3.onTimeInMsForPnoScan);
EXPECT_EQ(legacy_stats.radios[i].stats.on_time_hs20,
- converted.radios[i].onTimeInMsForHs20Scan);
+ converted.radios[i].V1_3.onTimeInMsForHs20Scan);
EXPECT_EQ(legacy_stats.radios[i].channel_stats.size(),
- converted.radios[i].channelStats.size());
+ converted.radios[i].V1_3.channelStats.size());
for (size_t k = 0; k < legacy_stats.radios[i].channel_stats.size();
k++) {
auto& legacy_channel_st = legacy_stats.radios[i].channel_stats[k];
EXPECT_EQ(WifiChannelWidthInMhz::WIDTH_20,
- converted.radios[i].channelStats[k].channel.width);
- EXPECT_EQ(WifiChannelInMhz(legacy_channel_st.channel.center_freq),
- converted.radios[i].channelStats[k].channel.centerFreq);
- EXPECT_EQ(WifiChannelInMhz(legacy_channel_st.channel.center_freq0),
- converted.radios[i].channelStats[k].channel.centerFreq0);
- EXPECT_EQ(WifiChannelInMhz(legacy_channel_st.channel.center_freq1),
- converted.radios[i].channelStats[k].channel.centerFreq1);
+ converted.radios[i].V1_3.channelStats[k].channel.width);
+ EXPECT_EQ(
+ WifiChannelInMhz(legacy_channel_st.channel.center_freq),
+ converted.radios[i].V1_3.channelStats[k].channel.centerFreq);
+ EXPECT_EQ(
+ WifiChannelInMhz(legacy_channel_st.channel.center_freq0),
+ converted.radios[i].V1_3.channelStats[k].channel.centerFreq0);
+ EXPECT_EQ(
+ WifiChannelInMhz(legacy_channel_st.channel.center_freq1),
+ converted.radios[i].V1_3.channelStats[k].channel.centerFreq1);
EXPECT_EQ(legacy_channel_st.cca_busy_time,
- converted.radios[i].channelStats[k].ccaBusyTimeInMs);
+ converted.radios[i].V1_3.channelStats[k].ccaBusyTimeInMs);
EXPECT_EQ(legacy_channel_st.on_time,
- converted.radios[i].channelStats[k].onTimeInMs);
+ converted.radios[i].V1_3.channelStats[k].onTimeInMs);
+ }
+ }
+
+ EXPECT_EQ(legacy_stats.peers.size(), converted.iface.peers.size());
+ for (size_t i = 0; i < legacy_stats.peers.size(); i++) {
+ EXPECT_EQ(legacy_stats.peers[i].peer_info.bssload.sta_count,
+ converted.iface.peers[i].staCount);
+ EXPECT_EQ(legacy_stats.peers[i].peer_info.bssload.chan_util,
+ converted.iface.peers[i].chanUtil);
+ for (size_t j = 0; j < legacy_stats.peers[i].rate_stats.size(); j++) {
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.preamble,
+ (uint32_t)converted.iface.peers[i]
+ .rateStats[j]
+ .rateInfo.preamble);
+ EXPECT_EQ(
+ legacy_stats.peers[i].rate_stats[j].rate.nss,
+ (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.nss);
+ EXPECT_EQ(
+ legacy_stats.peers[i].rate_stats[j].rate.bw,
+ (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.bw);
+ EXPECT_EQ(
+ legacy_stats.peers[i].rate_stats[j].rate.rateMcsIdx,
+ converted.iface.peers[i].rateStats[j].rateInfo.rateMcsIdx);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].tx_mpdu,
+ converted.iface.peers[i].rateStats[j].txMpdu);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rx_mpdu,
+ converted.iface.peers[i].rateStats[j].rxMpdu);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].mpdu_lost,
+ converted.iface.peers[i].rateStats[j].mpduLost);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].retries,
+ converted.iface.peers[i].rateStats[j].retries);
}
}
}
diff --git a/wifi/1.5/default/tests/mock_wifi_iface_util.cpp b/wifi/1.5/default/tests/mock_wifi_iface_util.cpp
index fe6e9e2..b101c4a 100644
--- a/wifi/1.5/default/tests/mock_wifi_iface_util.cpp
+++ b/wifi/1.5/default/tests/mock_wifi_iface_util.cpp
@@ -29,8 +29,9 @@
namespace iface_util {
MockWifiIfaceUtil::MockWifiIfaceUtil(
- const std::weak_ptr<wifi_system::InterfaceTool> iface_tool)
- : WifiIfaceUtil(iface_tool) {}
+ const std::weak_ptr<wifi_system::InterfaceTool> iface_tool,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal)
+ : WifiIfaceUtil(iface_tool, legacy_hal) {}
} // namespace iface_util
} // namespace implementation
} // namespace V1_5
diff --git a/wifi/1.5/default/tests/mock_wifi_iface_util.h b/wifi/1.5/default/tests/mock_wifi_iface_util.h
index a719fec..6d5f59c 100644
--- a/wifi/1.5/default/tests/mock_wifi_iface_util.h
+++ b/wifi/1.5/default/tests/mock_wifi_iface_util.h
@@ -31,7 +31,8 @@
class MockWifiIfaceUtil : public WifiIfaceUtil {
public:
MockWifiIfaceUtil(
- const std::weak_ptr<wifi_system::InterfaceTool> iface_tool);
+ const std::weak_ptr<wifi_system::InterfaceTool> iface_tool,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal);
MOCK_METHOD1(getFactoryMacAddress,
std::array<uint8_t, 6>(const std::string&));
MOCK_METHOD2(setMacAddress,
diff --git a/wifi/1.5/default/tests/mock_wifi_legacy_hal.h b/wifi/1.5/default/tests/mock_wifi_legacy_hal.h
index 9ab2fd5..826b35f 100644
--- a/wifi/1.5/default/tests/mock_wifi_legacy_hal.h
+++ b/wifi/1.5/default/tests/mock_wifi_legacy_hal.h
@@ -62,6 +62,7 @@
wifi_error(const std::string& ifname,
wifi_interface_type iftype));
MOCK_METHOD1(deleteVirtualInterface, wifi_error(const std::string& ifname));
+ MOCK_METHOD0(waitForDriverReady, wifi_error());
};
} // namespace legacy_hal
} // namespace implementation
diff --git a/wifi/1.5/default/tests/wifi_chip_unit_tests.cpp b/wifi/1.5/default/tests/wifi_chip_unit_tests.cpp
index d99bfbd..0ad6f3e 100644
--- a/wifi/1.5/default/tests/wifi_chip_unit_tests.cpp
+++ b/wifi/1.5/default/tests/wifi_chip_unit_tests.cpp
@@ -276,7 +276,7 @@
std::shared_ptr<NiceMock<mode_controller::MockWifiModeController>>
mode_controller_{new NiceMock<mode_controller::MockWifiModeController>};
std::shared_ptr<NiceMock<iface_util::MockWifiIfaceUtil>> iface_util_{
- new NiceMock<iface_util::MockWifiIfaceUtil>(iface_tool_)};
+ new NiceMock<iface_util::MockWifiIfaceUtil>(iface_tool_, legacy_hal_)};
std::shared_ptr<NiceMock<feature_flags::MockWifiFeatureFlags>>
feature_flags_{new NiceMock<feature_flags::MockWifiFeatureFlags>};
diff --git a/wifi/1.5/default/tests/wifi_iface_util_unit_tests.cpp b/wifi/1.5/default/tests/wifi_iface_util_unit_tests.cpp
index d70e42f..8b67bb8 100644
--- a/wifi/1.5/default/tests/wifi_iface_util_unit_tests.cpp
+++ b/wifi/1.5/default/tests/wifi_iface_util_unit_tests.cpp
@@ -22,6 +22,7 @@
#include "wifi_iface_util.h"
#include "mock_interface_tool.h"
+#include "mock_wifi_legacy_hal.h"
using testing::NiceMock;
using testing::Test;
@@ -48,7 +49,11 @@
protected:
std::shared_ptr<NiceMock<wifi_system::MockInterfaceTool>> iface_tool_{
new NiceMock<wifi_system::MockInterfaceTool>};
- WifiIfaceUtil* iface_util_ = new WifiIfaceUtil(iface_tool_);
+ legacy_hal::wifi_hal_fn fake_func_table_;
+ std::shared_ptr<NiceMock<legacy_hal::MockWifiLegacyHal>> legacy_hal_{
+ new NiceMock<legacy_hal::MockWifiLegacyHal>(iface_tool_,
+ fake_func_table_, true)};
+ WifiIfaceUtil* iface_util_ = new WifiIfaceUtil(iface_tool_, legacy_hal_);
};
TEST_F(WifiIfaceUtilTest, GetOrCreateRandomMacAddress) {
diff --git a/wifi/1.5/default/tests/wifi_nan_iface_unit_tests.cpp b/wifi/1.5/default/tests/wifi_nan_iface_unit_tests.cpp
index 52f0c2b..32da55e 100644
--- a/wifi/1.5/default/tests/wifi_nan_iface_unit_tests.cpp
+++ b/wifi/1.5/default/tests/wifi_nan_iface_unit_tests.cpp
@@ -122,7 +122,7 @@
new NiceMock<legacy_hal::MockWifiLegacyHal>(iface_tool_,
fake_func_table_, true)};
std::shared_ptr<NiceMock<iface_util::MockWifiIfaceUtil>> iface_util_{
- new NiceMock<iface_util::MockWifiIfaceUtil>(iface_tool_)};
+ new NiceMock<iface_util::MockWifiIfaceUtil>(iface_tool_, legacy_hal_)};
};
TEST_F(WifiNanIfaceTest, IfacEventHandlers_OnStateToggleOffOn) {
diff --git a/wifi/1.5/default/wifi.cpp b/wifi/1.5/default/wifi.cpp
index 17db51d..b9f20a4 100644
--- a/wifi/1.5/default/wifi.cpp
+++ b/wifi/1.5/default/wifi.cpp
@@ -37,12 +37,10 @@
const std::shared_ptr<wifi_system::InterfaceTool> iface_tool,
const std::shared_ptr<legacy_hal::WifiLegacyHalFactory> legacy_hal_factory,
const std::shared_ptr<mode_controller::WifiModeController> mode_controller,
- const std::shared_ptr<iface_util::WifiIfaceUtil> iface_util,
const std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags)
: iface_tool_(iface_tool),
legacy_hal_factory_(legacy_hal_factory),
mode_controller_(mode_controller),
- iface_util_(iface_util),
feature_flags_(feature_flags),
run_state_(RunState::STOPPED) {}
@@ -52,13 +50,21 @@
}
Return<void> Wifi::registerEventCallback(
- const sp<IWifiEventCallback>& event_callback,
+ const sp<V1_0::IWifiEventCallback>& event_callback,
registerEventCallback_cb hidl_status_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN,
&Wifi::registerEventCallbackInternal, hidl_status_cb,
event_callback);
}
+Return<void> Wifi::registerEventCallback_1_5(
+ const sp<V1_5::IWifiEventCallback>& event_callback,
+ registerEventCallback_1_5_cb hidl_status_cb) {
+ return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN,
+ &Wifi::registerEventCallbackInternal_1_5,
+ hidl_status_cb, event_callback);
+}
+
Return<bool> Wifi::isStarted() { return run_state_ != RunState::STOPPED; }
Return<void> Wifi::start(start_cb hidl_status_cb) {
@@ -97,7 +103,13 @@
}
WifiStatus Wifi::registerEventCallbackInternal(
- const sp<IWifiEventCallback>& event_callback) {
+ const sp<V1_0::IWifiEventCallback>& event_callback __unused) {
+ // Deprecated support for this callback.
+ return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED);
+}
+
+WifiStatus Wifi::registerEventCallbackInternal_1_5(
+ const sp<V1_5::IWifiEventCallback>& event_callback) {
if (!event_cb_handler_.addCallback(event_callback)) {
return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
}
@@ -119,7 +131,7 @@
WifiStatus wifi_status =
createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, error);
for (const auto& callback : event_cb_handler_.getCallbacks()) {
- if (!callback->onFailure(wifi_status).isOk()) {
+ if (!callback->onSubsystemRestart(wifi_status).isOk()) {
LOG(ERROR) << "Failed to invoke onFailure callback";
}
}
@@ -130,7 +142,8 @@
for (auto& hal : legacy_hals_) {
chips_.push_back(new WifiChip(
chipId, chipId == kPrimaryChipId, hal, mode_controller_,
- iface_util_, feature_flags_, on_subsystem_restart_callback));
+ std::make_shared<iface_util::WifiIfaceUtil>(iface_tool_, hal),
+ feature_flags_, on_subsystem_restart_callback));
chipId++;
}
run_state_ = RunState::STARTED;
diff --git a/wifi/1.5/default/wifi.h b/wifi/1.5/default/wifi.h
index 9f5a1b0..840bdfd 100644
--- a/wifi/1.5/default/wifi.h
+++ b/wifi/1.5/default/wifi.h
@@ -46,15 +46,17 @@
legacy_hal_factory,
const std::shared_ptr<mode_controller::WifiModeController>
mode_controller,
- const std::shared_ptr<iface_util::WifiIfaceUtil> iface_util,
const std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags);
bool isValid();
// HIDL methods exposed.
Return<void> registerEventCallback(
- const sp<IWifiEventCallback>& event_callback,
+ const sp<V1_0::IWifiEventCallback>& event_callback,
registerEventCallback_cb hidl_status_cb) override;
+ Return<void> registerEventCallback_1_5(
+ const sp<V1_5::IWifiEventCallback>& event_callback,
+ registerEventCallback_1_5_cb hidl_status_cb) override;
Return<bool> isStarted() override;
Return<void> start(start_cb hidl_status_cb) override;
Return<void> stop(stop_cb hidl_status_cb) override;
@@ -68,7 +70,9 @@
// Corresponding worker functions for the HIDL methods.
WifiStatus registerEventCallbackInternal(
- const sp<IWifiEventCallback>& event_callback);
+ const sp<V1_0::IWifiEventCallback>& event_callback __unused);
+ WifiStatus registerEventCallbackInternal_1_5(
+ const sp<V1_5::IWifiEventCallback>& event_callback);
WifiStatus startInternal();
WifiStatus stopInternal(std::unique_lock<std::recursive_mutex>* lock);
std::pair<WifiStatus, std::vector<ChipId>> getChipIdsInternal();
@@ -85,11 +89,10 @@
std::shared_ptr<legacy_hal::WifiLegacyHalFactory> legacy_hal_factory_;
std::shared_ptr<mode_controller::WifiModeController> mode_controller_;
std::vector<std::shared_ptr<legacy_hal::WifiLegacyHal>> legacy_hals_;
- std::shared_ptr<iface_util::WifiIfaceUtil> iface_util_;
std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags_;
RunState run_state_;
std::vector<sp<WifiChip>> chips_;
- hidl_callback_util::HidlCallbackHandler<IWifiEventCallback>
+ hidl_callback_util::HidlCallbackHandler<V1_5::IWifiEventCallback>
event_cb_handler_;
DISALLOW_COPY_AND_ASSIGN(Wifi);
diff --git a/wifi/1.5/default/wifi_chip.cpp b/wifi/1.5/default/wifi_chip.cpp
index 0450a7b..961f9da 100644
--- a/wifi/1.5/default/wifi_chip.cpp
+++ b/wifi/1.5/default/wifi_chip.cpp
@@ -353,7 +353,7 @@
ChipId chip_id, bool is_primary,
const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
const std::weak_ptr<mode_controller::WifiModeController> mode_controller,
- const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util,
+ const std::shared_ptr<iface_util::WifiIfaceUtil> iface_util,
const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags,
const std::function<void(const std::string&)>& handler)
: chip_id_(chip_id),
@@ -747,6 +747,13 @@
ifaceModeMask, filterMask);
}
+Return<void> WifiChip::triggerSubsystemRestart(
+ triggerSubsystemRestart_cb hidl_status_cb) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::triggerSubsystemRestartInternal,
+ hidl_status_cb);
+}
+
void WifiChip::invalidateAndRemoveAllIfaces() {
invalidateAndClearBridgedApAll();
invalidateAndClearAll(ap_ifaces_);
@@ -986,14 +993,14 @@
}
}
br_ifaces_ap_instances_[br_ifname] = ap_instances;
- if (!iface_util_.lock()->createBridge(br_ifname)) {
+ if (!iface_util_->createBridge(br_ifname)) {
LOG(ERROR) << "Failed createBridge - br_name=" << br_ifname.c_str();
invalidateAndClearBridgedAp(br_ifname);
return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
}
for (auto const& instance : ap_instances) {
// Bind ap instance interface to AP bridge
- if (!iface_util_.lock()->addIfaceToBridge(br_ifname, instance)) {
+ if (!iface_util_->addIfaceToBridge(br_ifname, instance)) {
LOG(ERROR) << "Failed add if to Bridge - if_name="
<< instance.c_str();
invalidateAndClearBridgedAp(br_ifname);
@@ -1054,8 +1061,7 @@
if (it.first == ifname) {
for (auto const& iface : it.second) {
if (iface == ifInstanceName) {
- if (!iface_util_.lock()->removeIfaceFromBridge(it.first,
- iface)) {
+ if (!iface_util_->removeIfaceFromBridge(it.first, iface)) {
LOG(ERROR)
<< "Failed to remove interface: " << ifInstanceName
<< " from " << ifname;
@@ -1086,7 +1092,7 @@
}
bool is_dedicated_iface = true;
std::string ifname = getPredefinedNanIfaceName();
- if (ifname.empty() || !iface_util_.lock()->ifNameToIndex(ifname)) {
+ if (ifname.empty() || !iface_util_->ifNameToIndex(ifname)) {
// Use the first shared STA iface (wlan0) if a dedicated aware iface is
// not defined.
ifname = getFirstActiveWlanIfaceName();
@@ -1523,6 +1529,11 @@
return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_usable_channels};
}
+WifiStatus WifiChip::triggerSubsystemRestartInternal() {
+ auto legacy_status = legacy_hal_.lock()->triggerSubsystemRestart();
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
WifiStatus WifiChip::handleChipConfiguration(
/* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
ChipModeId mode_id) {
@@ -1968,10 +1979,10 @@
void WifiChip::invalidateAndClearBridgedApAll() {
for (auto const& it : br_ifaces_ap_instances_) {
for (auto const& iface : it.second) {
- iface_util_.lock()->removeIfaceFromBridge(it.first, iface);
+ iface_util_->removeIfaceFromBridge(it.first, iface);
legacy_hal_.lock()->deleteVirtualInterface(iface);
}
- iface_util_.lock()->deleteBridge(it.first);
+ iface_util_->deleteBridge(it.first);
}
br_ifaces_ap_instances_.clear();
}
@@ -1982,10 +1993,10 @@
for (auto const& it : br_ifaces_ap_instances_) {
if (it.first == br_name) {
for (auto const& iface : it.second) {
- iface_util_.lock()->removeIfaceFromBridge(br_name, iface);
+ iface_util_->removeIfaceFromBridge(br_name, iface);
legacy_hal_.lock()->deleteVirtualInterface(iface);
}
- iface_util_.lock()->deleteBridge(br_name);
+ iface_util_->deleteBridge(br_name);
br_ifaces_ap_instances_.erase(br_name);
break;
}
diff --git a/wifi/1.5/default/wifi_chip.h b/wifi/1.5/default/wifi_chip.h
index b4ed30e..bd40ead 100644
--- a/wifi/1.5/default/wifi_chip.h
+++ b/wifi/1.5/default/wifi_chip.h
@@ -54,7 +54,7 @@
const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
const std::weak_ptr<mode_controller::WifiModeController>
mode_controller,
- const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util,
+ const std::shared_ptr<iface_util::WifiIfaceUtil> iface_util,
const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags,
const std::function<void(const std::string&)>&
subsystemCallbackHandler);
@@ -184,6 +184,8 @@
WifiBand band, hidl_bitfield<WifiIfaceMode> ifaceModeMask,
hidl_bitfield<UsableChannelFilter> filterMask,
getUsableChannels_cb _hidl_cb) override;
+ Return<void> triggerSubsystemRestart(
+ triggerSubsystemRestart_cb hidl_status_cb) override;
private:
void invalidateAndRemoveAllIfaces();
@@ -303,11 +305,12 @@
void invalidateAndClearBridgedApAll();
void invalidateAndClearBridgedAp(const std::string& br_name);
bool findUsingNameFromBridgedApInstances(const std::string& name);
+ WifiStatus triggerSubsystemRestartInternal();
ChipId chip_id_;
std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
std::weak_ptr<mode_controller::WifiModeController> mode_controller_;
- std::weak_ptr<iface_util::WifiIfaceUtil> iface_util_;
+ std::shared_ptr<iface_util::WifiIfaceUtil> iface_util_;
std::vector<sp<WifiApIface>> ap_ifaces_;
std::vector<sp<WifiNanIface>> nan_ifaces_;
std::vector<sp<WifiP2pIface>> p2p_ifaces_;
diff --git a/wifi/1.5/default/wifi_iface_util.cpp b/wifi/1.5/default/wifi_iface_util.cpp
index 2a0aef8..d1434e3 100644
--- a/wifi/1.5/default/wifi_iface_util.cpp
+++ b/wifi/1.5/default/wifi_iface_util.cpp
@@ -41,8 +41,10 @@
namespace iface_util {
WifiIfaceUtil::WifiIfaceUtil(
- const std::weak_ptr<wifi_system::InterfaceTool> iface_tool)
+ const std::weak_ptr<wifi_system::InterfaceTool> iface_tool,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal)
: iface_tool_(iface_tool),
+ legacy_hal_(legacy_hal),
random_mac_address_(nullptr),
event_handlers_map_() {}
@@ -59,14 +61,20 @@
return false;
}
#endif
- if (!iface_tool_.lock()->SetMacAddress(iface_name.c_str(), mac)) {
- LOG(ERROR) << "SetMacAddress failed.";
- return false;
- }
+ bool success = iface_tool_.lock()->SetMacAddress(iface_name.c_str(), mac);
#ifndef WIFI_AVOID_IFACE_RESET_MAC_CHANGE
if (!iface_tool_.lock()->SetUpState(iface_name.c_str(), true)) {
- LOG(ERROR) << "SetUpState(true) failed.";
- return false;
+ LOG(ERROR) << "SetUpState(true) failed. Wait for driver ready.";
+ // Wait for driver ready and try to set iface UP again
+ if (legacy_hal_.lock()->waitForDriverReady() !=
+ legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "SetUpState(true) wait for driver ready failed.";
+ return false;
+ }
+ if (!iface_tool_.lock()->SetUpState(iface_name.c_str(), true)) {
+ LOG(ERROR) << "SetUpState(true) failed after retry.";
+ return false;
+ }
}
#endif
IfaceEventHandlers event_handlers = {};
@@ -77,8 +85,12 @@
if (event_handlers.on_state_toggle_off_on != nullptr) {
event_handlers.on_state_toggle_off_on(iface_name);
}
- LOG(DEBUG) << "Successfully SetMacAddress.";
- return true;
+ if (!success) {
+ LOG(ERROR) << "SetMacAddress failed.";
+ } else {
+ LOG(DEBUG) << "SetMacAddress succeeded.";
+ }
+ return success;
}
std::array<uint8_t, 6> WifiIfaceUtil::getOrCreateRandomMacAddress() {
diff --git a/wifi/1.5/default/wifi_iface_util.h b/wifi/1.5/default/wifi_iface_util.h
index 296d182..b449077 100644
--- a/wifi/1.5/default/wifi_iface_util.h
+++ b/wifi/1.5/default/wifi_iface_util.h
@@ -21,6 +21,8 @@
#include <android/hardware/wifi/1.0/IWifi.h>
+#include "wifi_legacy_hal.h"
+
namespace android {
namespace hardware {
namespace wifi {
@@ -40,7 +42,8 @@
*/
class WifiIfaceUtil {
public:
- WifiIfaceUtil(const std::weak_ptr<wifi_system::InterfaceTool> iface_tool);
+ WifiIfaceUtil(const std::weak_ptr<wifi_system::InterfaceTool> iface_tool,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal);
virtual ~WifiIfaceUtil() = default;
virtual std::array<uint8_t, 6> getFactoryMacAddress(
@@ -73,6 +76,7 @@
std::array<uint8_t, 6> createRandomMacAddress();
std::weak_ptr<wifi_system::InterfaceTool> iface_tool_;
+ std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
std::unique_ptr<std::array<uint8_t, 6>> random_mac_address_;
std::map<std::string, IfaceEventHandlers> event_handlers_map_;
};
diff --git a/wifi/1.5/default/wifi_legacy_hal.cpp b/wifi/1.5/default/wifi_legacy_hal.cpp
index f5ca753..848fbd6 100644
--- a/wifi/1.5/default/wifi_legacy_hal.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal.cpp
@@ -476,6 +476,10 @@
bool WifiLegacyHal::isStarted() { return is_started_; }
+wifi_error WifiLegacyHal::waitForDriverReady() {
+ return global_func_table_.wifi_wait_for_driver_ready();
+}
+
std::pair<wifi_error, std::string> WifiLegacyHal::getDriverVersion(
const std::string& iface_name) {
std::array<char, kMaxVersionStringLength> buffer;
@@ -715,9 +719,29 @@
wifi_iface_stat* iface_stats_ptr, int num_radios,
wifi_radio_stat* radio_stats_ptr) {
wifi_radio_stat* l_radio_stats_ptr;
+ wifi_peer_info* l_peer_info_stats_ptr;
if (iface_stats_ptr != nullptr) {
link_stats_ptr->iface = *iface_stats_ptr;
+ l_peer_info_stats_ptr = iface_stats_ptr->peer_info;
+ for (uint32_t i = 0; i < iface_stats_ptr->num_peers; i++) {
+ WifiPeerInfo peer;
+ peer.peer_info = *l_peer_info_stats_ptr;
+ if (l_peer_info_stats_ptr->num_rate > 0) {
+ /* Copy the rate stats */
+ peer.rate_stats.assign(
+ l_peer_info_stats_ptr->rate_stats,
+ l_peer_info_stats_ptr->rate_stats +
+ l_peer_info_stats_ptr->num_rate);
+ }
+ peer.peer_info.num_rate = 0;
+ link_stats_ptr->peers.push_back(peer);
+ l_peer_info_stats_ptr =
+ (wifi_peer_info*)((u8*)l_peer_info_stats_ptr +
+ sizeof(wifi_peer_info) +
+ (sizeof(wifi_rate_stat) *
+ l_peer_info_stats_ptr->num_rate));
+ }
link_stats_ptr->iface.num_peers = 0;
} else {
LOG(ERROR) << "Invalid iface stats in link layer stats";
@@ -1652,6 +1676,10 @@
return {status, std::move(channels)};
}
+wifi_error WifiLegacyHal::triggerSubsystemRestart() {
+ return global_func_table_.wifi_trigger_subsystem_restart();
+}
+
void WifiLegacyHal::invalidate() {
global_handle_ = nullptr;
iface_name_to_handle_.clear();
diff --git a/wifi/1.5/default/wifi_legacy_hal.h b/wifi/1.5/default/wifi_legacy_hal.h
index 03ca841..2bb7631 100644
--- a/wifi/1.5/default/wifi_legacy_hal.h
+++ b/wifi/1.5/default/wifi_legacy_hal.h
@@ -340,9 +340,15 @@
std::vector<wifi_channel_stat> channel_stats;
};
+struct WifiPeerInfo {
+ wifi_peer_info peer_info;
+ std::vector<wifi_rate_stat> rate_stats;
+};
+
struct LinkLayerStats {
wifi_iface_stat iface;
std::vector<LinkLayerRadioStats> radios;
+ std::vector<WifiPeerInfo> peers;
};
#pragma GCC diagnostic pop
@@ -473,6 +479,7 @@
// using a predefined timeout.
virtual wifi_error stop(std::unique_lock<std::recursive_mutex>* lock,
const std::function<void()>& on_complete_callback);
+ virtual wifi_error waitForDriverReady();
// Checks if legacy HAL has successfully started
bool isStarted();
// Wrappers for all the functions in the legacy HAL function table.
@@ -709,6 +716,8 @@
std::pair<wifi_error, std::vector<wifi_usable_channel>> getUsableChannels(
uint32_t band_mask, uint32_t iface_mode_mask, uint32_t filter_mask);
+ wifi_error triggerSubsystemRestart();
+
private:
// Retrieve interface handles for all the available interfaces.
wifi_error retrieveIfaceHandles();
diff --git a/wifi/1.5/default/wifi_legacy_hal_stubs.cpp b/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
index 6212960..dd860d6 100644
--- a/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
@@ -160,6 +160,7 @@
populateStubFor(&hal_fn->wifi_twt_clear_stats);
populateStubFor(&hal_fn->wifi_set_dtim_config);
populateStubFor(&hal_fn->wifi_get_usable_channels);
+ populateStubFor(&hal_fn->wifi_trigger_subsystem_restart);
return true;
}
} // namespace legacy_hal
diff --git a/wifi/1.5/types.hal b/wifi/1.5/types.hal
index e1c0d32..3c19791 100644
--- a/wifi/1.5/types.hal
+++ b/wifi/1.5/types.hal
@@ -26,6 +26,7 @@
import @1.3::StaLinkLayerRadioStats;
import @1.0::WifiChannelInMhz;
import @1.0::WifiChannelWidthInMhz;
+import @1.4::WifiRateInfo;
/**
* Wifi bands defined in 80211 spec.
@@ -162,6 +163,54 @@
};
/**
+ * Per rate statistics. The rate is characterized by the combination of preamble, number of spatial
+ * streams, transmission bandwidth, and modulation and coding scheme (MCS).
+ */
+struct StaRateStat{
+ /**
+ * Wifi rate information: preamble, number of spatial streams, bandwidth, MCS, etc.
+ */
+ WifiRateInfo rateInfo;
+ /**
+ * Number of successfully transmitted data packets (ACK received)
+ */
+ uint32_t txMpdu;
+ /**
+ * Number of received data packets
+ */
+ uint32_t rxMpdu;
+ /**
+ * Number of data packet losses (no ACK)
+ */
+ uint32_t mpduLost;
+ /**
+ * Number of data packet retries
+ */
+ uint32_t retries;
+};
+
+/**
+ * Per peer statistics. The types of peer include the Access Point (AP), the Tunneled Direct Link
+ * Setup (TDLS), the Group Owner (GO), the Neighbor Awareness Networking (NAN), etc.
+ */
+struct StaPeerInfo {
+ /**
+ * Station count: The total number of stations currently associated with the peer.
+ */
+ uint16_t staCount;
+ /**
+ * Channel utilization: The percentage of time (normalized to 255, i.e., x% corresponds to
+ * (int) x * 255 / 100) that the medium is sensed as busy measured by either physical or
+ * virtual carrier sense (CS) mechanism.
+ */
+ uint16_t chanUtil;
+ /**
+ * Per rate statistics
+ */
+ vec<StaRateStat> rateStats;
+};
+
+/**
* Iface statistics for the current connection.
*/
struct StaLinkLayerIfaceStats {
@@ -197,6 +246,26 @@
* WME Voice (VO) Access Category (AC) contention time statistics.
*/
StaLinkLayerIfaceContentionTimeStats wmeVoContentionTimeStats;
+
+ /**
+ * Per peer statistics.
+ */
+ vec<StaPeerInfo> peers;
+};
+
+struct StaLinkLayerRadioStats {
+ /**
+ * Baseline information as defined in HAL 1.3.
+ */
+ @1.3::StaLinkLayerRadioStats V1_3;
+
+ /**
+ * Radio ID: An implementation specific value identifying the radio interface for which the
+ * stats are produced. Framework must not interpret this value. It must use this value for
+ * persistently identifying the statistics between calls,
+ * e.g. if the HAL provides them in different order.
+ */
+ int32_t radioId;
};
/**
diff --git a/wifi/hostapd/1.3/IHostapdCallback.hal b/wifi/hostapd/1.3/IHostapdCallback.hal
index a098d87..3208366 100644
--- a/wifi/hostapd/1.3/IHostapdCallback.hal
+++ b/wifi/hostapd/1.3/IHostapdCallback.hal
@@ -35,12 +35,12 @@
* The apIfaceInstance can be used to identify which instance the callback
* is from.
* Note: The apIfaceInstance must be same as ifaceName in single AP mode.
- * @param freq The operational frequency of the AP.
+ * @param freqMhz The operational frequency of the AP in Mhz.
* @param bandwidth The operational bandwidth of the AP.
* @param generation The operational mode of the AP (e.g. 11ac, 11ax).
* @param apIfaceInstanceMacAddress MAC Address of the apIfaceInstance.
*/
- oneway onApInstanceInfoChanged(string ifaceName, string apIfaceInstance, uint32_t freq,
+ oneway onApInstanceInfoChanged(string ifaceName, string apIfaceInstance, uint32_t freqMhz,
Bandwidth bandwidth, Generation generation, MacAddress apIfaceInstanceMacAddress);
/**
diff --git a/wifi/hostapd/1.3/types.hal b/wifi/hostapd/1.3/types.hal
index f453df7..2230da8 100644
--- a/wifi/hostapd/1.3/types.hal
+++ b/wifi/hostapd/1.3/types.hal
@@ -25,7 +25,7 @@
* WIFI_STANDARD_11N = [hw_mode is HOSTAPD_MODE_IEEE80211G and (HT is 1 or HT40 is 1)] or
* [hw_mode is HOSTAPD_MODE_IEEE80211A and VHT is 0].
* WIFI_STANDARD_11AC = hw_mode is HOSTAPD_MODE_IEEE80211A and VHT is 1.
- * WIFI_STANDARD_11AX = hw_mode is HOSTAPD_MODE_IEEE80211AX.
+ * WIFI_STANDARD_11AX = hw_mode is HOSTAPD_MODE_IEEE80211A and HE supported.
* WIFI_STANDARD_11AD = hw_mode is HOSTAPD_MODE_IEEE80211AD.
*/
enum Generation : uint32_t {
diff --git a/wifi/supplicant/1.4/Android.bp b/wifi/supplicant/1.4/Android.bp
index b486687..c988fdb 100644
--- a/wifi/supplicant/1.4/Android.bp
+++ b/wifi/supplicant/1.4/Android.bp
@@ -16,6 +16,7 @@
"types.hal",
"ISupplicant.hal",
"ISupplicantP2pIface.hal",
+ "ISupplicantP2pIfaceCallback.hal",
"ISupplicantStaIface.hal",
"ISupplicantStaNetwork.hal",
"ISupplicantStaNetworkCallback.hal",
diff --git a/wifi/supplicant/1.4/ISupplicantP2pIface.hal b/wifi/supplicant/1.4/ISupplicantP2pIface.hal
index 65c761d..28846de 100644
--- a/wifi/supplicant/1.4/ISupplicantP2pIface.hal
+++ b/wifi/supplicant/1.4/ISupplicantP2pIface.hal
@@ -17,6 +17,7 @@
package android.hardware.wifi.supplicant@1.4;
import @1.2::ISupplicantP2pIface;
+import ISupplicantP2pIfaceCallback;
/**
* Interface exposed by the supplicant for each P2P mode network
@@ -48,4 +49,36 @@
* @return enabled true if set, false otherwise.
*/
getEdmg() generates (SupplicantStatus status, bool enabled);
+
+ /**
+ * Register for callbacks from this interface.
+ *
+ * These callbacks are invoked for events that are specific to this interface.
+ * Registration of multiple callback objects is supported. These objects must
+ * be automatically deleted when the corresponding client process is dead or
+ * if this interface is removed.
+ *
+ * @param callback An instance of the |ISupplicantP2pIfaceCallback| HIDL
+ * interface object.
+ * @return status Status of the operation.
+ * Possible status codes:
+ * |SupplicantStatusCode.SUCCESS|,
+ * |SupplicantStatusCode.FAILURE_UNKNOWN|,
+ * |SupplicantStatusCode.FAILURE_IFACE_INVALID|
+ */
+ registerCallback_1_4(ISupplicantP2pIfaceCallback callback)
+ generates (SupplicantStatus status);
+
+ /*
+ * Set Wifi Display R2 device info.
+ *
+ * @param info WFD R2 device info as described in section 5.1.12 of WFD technical
+ * specification v2.1.
+ * @return status Status of the operation.
+ * Possible status codes:
+ * |SupplicantStatusCode.SUCCESS|,
+ * |SupplicantStatusCode.FAILURE_UNKNOWN|,
+ * |SupplicantStatusCode.FAILURE_IFACE_INVALID|
+ */
+ setWfdR2DeviceInfo(uint8_t[4] info) generates (SupplicantStatus status);
};
diff --git a/wifi/supplicant/1.4/ISupplicantP2pIfaceCallback.hal b/wifi/supplicant/1.4/ISupplicantP2pIfaceCallback.hal
new file mode 100644
index 0000000..a091274
--- /dev/null
+++ b/wifi/supplicant/1.4/ISupplicantP2pIfaceCallback.hal
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package android.hardware.wifi.supplicant@1.4;
+
+import @1.0::ISupplicantP2pIfaceCallback;
+import @1.0::MacAddress;
+import @1.0::WpsConfigMethods;
+import @1.0::P2pGroupCapabilityMask;
+
+/**
+ * Callback Interface exposed by the supplicant service
+ * for each P2P mode interface (ISupplicantP2pIface).
+ *
+ * Clients need to host an instance of this HIDL interface object and
+ * pass a reference of the object to the supplicant via the
+ * corresponding |ISupplicantP2pIface.registerCallback| method.
+ */
+interface ISupplicantP2pIfaceCallback extends @1.0::ISupplicantP2pIfaceCallback {
+ /**
+ * Used to indicate that a P2P Wi-Fi Display R2 device has been found. Refer to
+ * Wi-Fi Display Technical Specification Version 2.0.
+ *
+ * @param srcAddress MAC address of the device found. This must either
+ * be the P2P device address for a peer which is not in a group,
+ * or the P2P interface address for a peer which is a Group Owner.
+ * @param p2pDeviceAddress P2P device address.
+ * @param primaryDeviceType Type of device. Refer to section B.1 of Wifi P2P
+ * Technical specification v1.2.
+ * @param deviceName Name of the device.
+ * @param configMethods Mask of WPS configuration methods supported by the
+ * device.
+ * @param deviceCapabilities Refer to section 4.1.4 of Wifi P2P Technical
+ * specification v1.2.
+ * @param groupCapabilites Refer to section 4.1.4 of Wifi P2P Technical
+ * specification v1.2.
+ * @param wfdDeviceInfo WFD device info as described in section 5.1.2 of WFD
+ * technical specification v1.0.0.
+ * @param wfdR2DeviceInfo WFD R2 device info as described in section 5.1.12 of WFD
+ * technical specification v2.1.
+ */
+ oneway onR2DeviceFound(
+ MacAddress srcAddress, MacAddress p2pDeviceAddress,
+ uint8_t[8] primaryDeviceType, string deviceName,
+ bitfield<WpsConfigMethods> configMethods, uint8_t deviceCapabilities,
+ bitfield<P2pGroupCapabilityMask> groupCapabilities, uint8_t[6] wfdDeviceInfo,
+ uint8_t[2] wfdR2DeviceInfo);
+};
diff --git a/wifi/supplicant/1.4/types.hal b/wifi/supplicant/1.4/types.hal
index c39de6e..b72eb42 100644
--- a/wifi/supplicant/1.4/types.hal
+++ b/wifi/supplicant/1.4/types.hal
@@ -107,6 +107,10 @@
* WPA3 SAE Public-Key.
*/
SAE_PK = 1 << 2,
+ /**
+ * Wi-Fi Display R2
+ */
+ WFD_R2 = 1 << 3,
};
/**
diff --git a/wifi/supplicant/1.4/vts/functional/supplicant_p2p_iface_hidl_test.cpp b/wifi/supplicant/1.4/vts/functional/supplicant_p2p_iface_hidl_test.cpp
index 9185279..4427c390 100644
--- a/wifi/supplicant/1.4/vts/functional/supplicant_p2p_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.4/vts/functional/supplicant_p2p_iface_hidl_test.cpp
@@ -28,16 +28,23 @@
#include "supplicant_hidl_test_utils_1_4.h"
using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatus;
using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode;
using ::android::hardware::wifi::supplicant::V1_4::ISupplicantP2pIface;
+using ::android::hardware::wifi::supplicant::V1_4::ISupplicantP2pIfaceCallback;
using SupplicantStatusV1_4 =
::android::hardware::wifi::supplicant::V1_4::SupplicantStatus;
using SupplicantStatusCodeV1_4 =
::android::hardware::wifi::supplicant::V1_4::SupplicantStatusCode;
+constexpr uint8_t kTestWfdR2DeviceInfo[] = {[0 ... 3] = 0x01};
+
class SupplicantP2pIfaceHidlTest : public SupplicantHidlTestBaseV1_4 {
public:
virtual void SetUp() override {
@@ -51,6 +58,100 @@
sp<ISupplicantP2pIface> p2p_iface_;
};
+class IfaceCallback : public ISupplicantP2pIfaceCallback {
+ Return<void> onNetworkAdded(uint32_t /* id */) override { return Void(); }
+ Return<void> onNetworkRemoved(uint32_t /* id */) override { return Void(); }
+ Return<void> onDeviceFound(
+ const hidl_array<uint8_t, 6>& /* srcAddress */,
+ const hidl_array<uint8_t, 6>& /* p2pDeviceAddress */,
+ const hidl_array<uint8_t, 8>& /* primaryDeviceType */,
+ const hidl_string& /* deviceName */, uint16_t /* configMethods */,
+ uint8_t /* deviceCapabilities */, uint32_t /* groupCapabilities */,
+ const hidl_array<uint8_t, 6>& /* wfdDeviceInfo */) override {
+ return Void();
+ }
+ Return<void> onDeviceLost(
+ const hidl_array<uint8_t, 6>& /* p2pDeviceAddress */) override {
+ return Void();
+ }
+ Return<void> onFindStopped() override { return Void(); }
+ Return<void> onGoNegotiationRequest(
+ const hidl_array<uint8_t, 6>& /* srcAddress */,
+ ISupplicantP2pIfaceCallback::WpsDevPasswordId /* passwordId */)
+ override {
+ return Void();
+ }
+ Return<void> onGoNegotiationCompleted(
+ ISupplicantP2pIfaceCallback::P2pStatusCode /* status */) override {
+ return Void();
+ }
+ Return<void> onGroupFormationSuccess() override { return Void(); }
+ Return<void> onGroupFormationFailure(
+ const hidl_string& /* failureReason */) override {
+ return Void();
+ }
+ Return<void> onGroupStarted(
+ const hidl_string& /* groupIfname */, bool /* isGo */,
+ const hidl_vec<uint8_t>& /* ssid */, uint32_t /* frequency */,
+ const hidl_array<uint8_t, 32>& /* psk */,
+ const hidl_string& /* passphrase */,
+ const hidl_array<uint8_t, 6>& /* goDeviceAddress */,
+ bool /* isPersistent */) override {
+ return Void();
+ }
+ Return<void> onGroupRemoved(const hidl_string& /* groupIfname */,
+ bool /* isGo */) override {
+ return Void();
+ }
+ Return<void> onInvitationReceived(
+ const hidl_array<uint8_t, 6>& /* srcAddress */,
+ const hidl_array<uint8_t, 6>& /* goDeviceAddress */,
+ const hidl_array<uint8_t, 6>& /* bssid */,
+ uint32_t /* persistentNetworkId */,
+ uint32_t /* operatingFrequency */) override {
+ return Void();
+ }
+ Return<void> onInvitationResult(
+ const hidl_array<uint8_t, 6>& /* bssid */,
+ ISupplicantP2pIfaceCallback::P2pStatusCode /* status */) override {
+ return Void();
+ }
+ Return<void> onProvisionDiscoveryCompleted(
+ const hidl_array<uint8_t, 6>& /* p2pDeviceAddress */,
+ bool /* isRequest */,
+ ISupplicantP2pIfaceCallback::P2pProvDiscStatusCode /* status */,
+ uint16_t /* configMethods */,
+ const hidl_string& /* generatedPin */) override {
+ return Void();
+ }
+ Return<void> onServiceDiscoveryResponse(
+ const hidl_array<uint8_t, 6>& /* srcAddress */,
+ uint16_t /* updateIndicator */,
+ const hidl_vec<uint8_t>& /* tlvs */) override {
+ return Void();
+ }
+ Return<void> onStaAuthorized(
+ const hidl_array<uint8_t, 6>& /* srcAddress */,
+ const hidl_array<uint8_t, 6>& /* p2pDeviceAddress */) override {
+ return Void();
+ }
+ Return<void> onStaDeauthorized(
+ const hidl_array<uint8_t, 6>& /* srcAddress */,
+ const hidl_array<uint8_t, 6>& /* p2pDeviceAddress */) override {
+ return Void();
+ }
+ Return<void> onR2DeviceFound(
+ const hidl_array<uint8_t, 6>& /* srcAddress */,
+ const hidl_array<uint8_t, 6>& /* p2pDeviceAddress */,
+ const hidl_array<uint8_t, 8>& /* primaryDeviceType */,
+ const hidl_string& /* deviceName */, uint16_t /* configMethods */,
+ uint8_t /* deviceCapabilities */, uint32_t /* groupCapabilities */,
+ const hidl_array<uint8_t, 6>& /* wfdDeviceInfo */,
+ const hidl_array<uint8_t, 2>& /* wfdR2DeviceInfo */) override {
+ return Void();
+ }
+};
+
/*
* SetGetEdmg
*/
@@ -71,6 +172,26 @@
});
}
+/*
+ * RegisterCallback_1_4
+ */
+TEST_P(SupplicantP2pIfaceHidlTest, RegisterCallback_1_4) {
+ p2p_iface_->registerCallback_1_4(
+ new IfaceCallback(), [](const SupplicantStatusV1_4& status) {
+ EXPECT_EQ(SupplicantStatusCodeV1_4::SUCCESS, status.code);
+ });
+}
+
+/*
+ * SetWfdR2DeviceInfo
+ */
+TEST_P(SupplicantP2pIfaceHidlTest, SetWfdR2DeviceInfo) {
+ p2p_iface_->setWfdR2DeviceInfo(
+ kTestWfdR2DeviceInfo, [&](const SupplicantStatusV1_4& status) {
+ EXPECT_EQ(SupplicantStatusCodeV1_4::SUCCESS, status.code);
+ });
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantP2pIfaceHidlTest);
INSTANTIATE_TEST_CASE_P(
PerInstance, SupplicantP2pIfaceHidlTest,