Merge "Fixed incorrect retry timer value type"
diff --git a/OWNERS b/OWNERS
index 433bbb7..3a1a038 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,9 +1,14 @@
per-file *.hal,*.aidl,OWNERS = set noparent
-per-file *.hal,*.aidl,OWNERS = elsk@google.com,malchev@google.com,smoreland@google.com
+per-file *.hal,*.aidl,OWNERS = devinmoore@google.com,elsk@google.com,malchev@google.com,smoreland@google.com
+# Android Native API Council
+devinmoore@google.com
elsk@google.com
-maco@google.com
malchev@google.com
smoreland@google.com
-yim@google.com # vts tests
-guangzhu@google.com # vts tests
+
+# historical/backup
+maco@google.com
+
+# vts tests
+guangzhu@google.com
diff --git a/audio/7.0/IDevice.hal b/audio/7.0/IDevice.hal
index e30e545..d9e0ad2 100644
--- a/audio/7.0/IDevice.hal
+++ b/audio/7.0/IDevice.hal
@@ -103,6 +103,11 @@
* If the stream can not be opened with the proposed audio config,
* HAL must provide suggested values for the audio config.
*
+ * Note: INVALID_ARGUMENTS is returned both in the case when the
+ * HAL can not use the provided config and in the case when
+ * the value of any argument is invalid. In the latter case the
+ * HAL must provide a default initialized suggested config.
+ *
* @param ioHandle handle assigned by AudioFlinger.
* @param device device type and (if needed) address.
* @param config stream configuration.
@@ -111,7 +116,8 @@
May be used by implementations to configure hardware effects.
* @return retval operation completion status.
* @return outStream created output stream.
- * @return suggestedConfig in case of invalid parameters, suggested config.
+ * @return suggestedConfig in the case of rejection of the proposed config,
+ * a config suggested by the HAL.
*/
openOutputStream(
AudioIoHandle ioHandle,
@@ -128,6 +134,11 @@
* If the stream can not be opened with the proposed audio config,
* HAL must provide suggested values for the audio config.
*
+ * Note: INVALID_ARGUMENTS is returned both in the case when the
+ * HAL can not use the provided config and in the case when
+ * the value of any argument is invalid. In the latter case the
+ * HAL must provide a default initialized suggested config.
+ *
* @param ioHandle handle assigned by AudioFlinger.
* @param device device type and (if needed) address.
* @param config stream configuration.
@@ -136,7 +147,8 @@
* May be used by implementations to configure processing effects.
* @return retval operation completion status.
* @return inStream in case of success, created input stream.
- * @return suggestedConfig in case of invalid parameters, suggested config.
+ * @return suggestedConfig in the case of rejection of the proposed config,
+ * a config suggested by the HAL.
*/
openInputStream(
AudioIoHandle ioHandle,
diff --git a/audio/7.0/IStream.hal b/audio/7.0/IStream.hal
index 4fe8218..ab9aa7d 100644
--- a/audio/7.0/IStream.hal
+++ b/audio/7.0/IStream.hal
@@ -66,9 +66,10 @@
* Retrieves basic stream configuration: sample rate, audio format,
* channel mask.
*
+ * @return retval operation completion status.
* @return config basic stream configuration.
*/
- getAudioProperties() generates (AudioConfigBase config);
+ getAudioProperties() generates (Result retval, AudioConfigBase config);
/**
* Sets stream parameters. Only sets parameters that are specified.
diff --git a/audio/7.0/IStreamIn.hal b/audio/7.0/IStreamIn.hal
index 0a3f24b..bf9ae52 100644
--- a/audio/7.0/IStreamIn.hal
+++ b/audio/7.0/IStreamIn.hal
@@ -41,6 +41,18 @@
setGain(float gain) generates (Result retval);
/**
+ * 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.
+ * @return retval operation completion status.
+ * If any of the metadata fields contains an invalid value,
+ * returns INVALID_ARGUMENTS.
+ * If method isn't supported by the HAL returns NOT_SUPPORTED.
+ */
+ updateSinkMetadata(SinkMetadata sinkMetadata) generates (Result retval);
+
+ /**
* Commands that can be executed on the driver reader thread.
*/
enum ReadCommand : int32_t {
@@ -82,12 +94,6 @@
};
/**
- * Called when the metadata of the stream's sink has been changed.
- * @param sinkMetadata Description of the audio that is suggested by the clients.
- */
- updateSinkMetadata(SinkMetadata sinkMetadata);
-
- /**
* Set up required transports for receiving audio buffers from the driver.
*
* The transport consists of three message queues:
diff --git a/audio/7.0/IStreamOut.hal b/audio/7.0/IStreamOut.hal
index 38d750f..4daab26 100644
--- a/audio/7.0/IStreamOut.hal
+++ b/audio/7.0/IStreamOut.hal
@@ -45,6 +45,18 @@
setVolume(float left, float right) generates (Result retval);
/**
+ * 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.
+ * @return retval operation completion status.
+ * If any of the metadata fields contains an invalid value,
+ * returns INVALID_ARGUMENTS.
+ * If method isn't supported by the HAL returns NOT_SUPPORTED.
+ */
+ updateSourceMetadata(SourceMetadata sourceMetadata) generates (Result retval);
+
+ /**
* Commands that can be executed on the driver writer thread.
*/
enum WriteCommand : int32_t {
@@ -77,12 +89,6 @@
};
/**
- * Called when the metadata of the stream's source has been changed.
- * @param sourceMetadata Description of the audio that is played by the clients.
- */
- updateSourceMetadata(SourceMetadata sourceMetadata);
-
- /**
* Set up required transports for passing audio buffers to the driver.
*
* The transport consists of three message queues:
diff --git a/audio/7.0/IStreamOutEventCallback.hal b/audio/7.0/IStreamOutEventCallback.hal
index 52e65d3..4de767f 100644
--- a/audio/7.0/IStreamOutEventCallback.hal
+++ b/audio/7.0/IStreamOutEventCallback.hal
@@ -42,7 +42,7 @@
* is TEST(metadata_tests, compatibility_R) [3]. The test for R compatibility for JNI
* marshalling is android.media.cts.AudioMetadataTest#testCompatibilityR [4].
*
- * R (audio HAL 7.0) defined keys are as follows [2]:
+ * R (audio HAL 6.0) defined keys are as follows [2]:
* "bitrate", int32
* "channel-mask", int32
* "mime", string
diff --git a/audio/7.0/config/api/current.txt b/audio/7.0/config/api/current.txt
index fea6979..1da8b09 100644
--- a/audio/7.0/config/api/current.txt
+++ b/audio/7.0/config/api/current.txt
@@ -360,7 +360,7 @@
public static class DevicePorts.DevicePort {
ctor public DevicePorts.DevicePort();
method @Nullable public String getAddress();
- method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioFormat> getEncodedFormats();
+ method @Nullable public java.util.List<java.lang.String> getEncodedFormats();
method @Nullable public android.audio.policy.configuration.V7_0.Gains getGains();
method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Profile> getProfile();
method @Nullable public android.audio.policy.configuration.V7_0.Role getRole();
@@ -368,7 +368,7 @@
method @Nullable public String getType();
method @Nullable public boolean get_default();
method public void setAddress(@Nullable String);
- method public void setEncodedFormats(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioFormat>);
+ method public void setEncodedFormats(@Nullable java.util.List<java.lang.String>);
method public void setGains(@Nullable android.audio.policy.configuration.V7_0.Gains);
method public void setRole(@Nullable android.audio.policy.configuration.V7_0.Role);
method public void setTagName(@Nullable String);
@@ -527,10 +527,10 @@
public static class SurroundFormats.Format {
ctor public SurroundFormats.Format();
- method @Nullable public android.audio.policy.configuration.V7_0.AudioFormat getName();
- method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioFormat> getSubformats();
- method public void setName(@Nullable android.audio.policy.configuration.V7_0.AudioFormat);
- method public void setSubformats(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioFormat>);
+ method @Nullable public String getName();
+ method @Nullable public java.util.List<java.lang.String> getSubformats();
+ method public void setName(@Nullable String);
+ method public void setSubformats(@Nullable java.util.List<java.lang.String>);
}
public class SurroundSound {
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
index 6784828..56b3a27 100644
--- a/audio/7.0/config/audio_policy_configuration.xsd
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -774,13 +774,13 @@
</xs:sequence>
</xs:complexType>
<xs:simpleType name="audioFormatsList">
- <xs:list itemType="audioFormat" />
+ <xs:list itemType="extendableAudioFormat" />
</xs:simpleType>
<xs:complexType name="surroundFormats">
<xs:sequence>
<xs:element name="format" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
- <xs:attribute name="name" type="audioFormat" use="required"/>
+ <xs:attribute name="name" type="extendableAudioFormat" use="required"/>
<xs:attribute name="subformats" type="audioFormatsList" />
</xs:complexType>
</xs:element>
diff --git a/audio/7.0/types.hal b/audio/7.0/types.hal
index 6cac9c9..2d42129 100644
--- a/audio/7.0/types.hal
+++ b/audio/7.0/types.hal
@@ -44,8 +44,10 @@
* A substitute for POSIX timespec.
*/
struct TimeSpec {
- uint64_t tvSec; // seconds
- uint64_t tvNSec; // nanoseconds
+ /** Seconds. */
+ uint64_t tvSec;
+ /** Nanoseconds. */
+ uint64_t tvNSec;
};
struct ParameterValue {
@@ -85,8 +87,10 @@
* Used by streams opened in mmap mode.
*/
struct MmapPosition {
- int64_t timeNanoseconds; // time stamp in ns, CLOCK_MONOTONIC
- int32_t positionFrames; // increasing 32 bit frame count reset when IStream.stop() is called
+ /** Timestamp in ns, CLOCK_MONOTONIC. */
+ int64_t timeNanoseconds;
+ /** Increasing 32 bit frame count reset when IStream.stop() is called. */
+ int32_t positionFrames;
};
/**
@@ -128,9 +132,12 @@
*/
@export(name="audio_microphone_channel_mapping_t", value_prefix="AUDIO_MICROPHONE_CHANNEL_MAPPING_")
enum AudioMicrophoneChannelMapping : uint32_t {
- UNUSED = 0, /* Channel not used */
- DIRECT = 1, /* Channel used and signal not processed */
- PROCESSED = 2, /* Channel used and signal has some processing */
+ /** Channel not used. */
+ UNUSED = 0,
+ /** Channel used and signal not processed. */
+ DIRECT = 1,
+ /** Channel used and signal has some processing. */
+ PROCESSED = 2,
};
/**
@@ -179,7 +186,8 @@
* Used by StreamIn and Device
*/
struct MicrophoneInfo {
- /** Unique alphanumeric id for microphone. Guaranteed to be the same
+ /**
+ * Unique alphanumeric id for microphone. Guaranteed to be the same
* even after rebooting.
*/
string deviceId;
@@ -187,18 +195,21 @@
* Device specific information
*/
DeviceAddress deviceAddress;
- /** Each element of the vector must describe the channel with the same
- * index.
+ /**
+ * Each element of the vector must describe the channel with the same
+ * index.
*/
vec<AudioMicrophoneChannelMapping> channelMapping;
/** Location of the microphone in regard to the body of the device */
AudioMicrophoneLocation location;
- /** Identifier to help group related microphones together
- * e.g. microphone arrays should belong to the same group
+ /**
+ * Identifier to help group related microphones together
+ * e.g. microphone arrays should belong to the same group
*/
AudioMicrophoneGroup group;
- /** Index of this microphone within the group.
- * (group, index) must be unique within the same device.
+ /**
+ * Index of this microphone within the group.
+ * (group, index) must be unique within the same device.
*/
uint32_t indexInTheGroup;
/** Level in dBFS produced by a 1000 Hz tone at 94 dB SPL */
@@ -209,17 +220,20 @@
float minSpl;
/** Standard polar pattern of the microphone */
AudioMicrophoneDirectionality directionality;
- /** Vector with ordered frequency responses (from low to high frequencies)
- * with the frequency response of the microphone.
- * Levels are in dB, relative to level at 1000 Hz
+ /**
+ * Vector with ordered frequency responses (from low to high frequencies)
+ * with the frequency response of the microphone.
+ * Levels are in dB, relative to level at 1000 Hz
*/
vec<AudioFrequencyResponsePoint> frequencyResponse;
- /** Position of the microphone's capsule in meters, from the
- * bottom-left-back corner of the bounding box of device.
+ /**
+ * Position of the microphone's capsule in meters, from the
+ * bottom-left-back corner of the bounding box of device.
*/
AudioMicrophoneCoordinate position;
- /** Normalized point to signal the main orientation of the microphone's
- * capsule. sqrt(x^2 + y^2 + z^2) = 1
+ /**
+ * Normalized point to signal the main orientation of the microphone's
+ * capsule. sqrt(x^2 + y^2 + z^2) = 1
*/
AudioMicrophoneCoordinate orientation;
};
@@ -262,7 +276,6 @@
// frameworks/base/media/java/android/media/AudioTrack.java
/**
* Disable any Dual Mono presentation effect.
- *
*/
OFF = 0,
/**
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 7148d76..c0042db 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
@@ -18,6 +18,8 @@
#define ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
#include <sys/types.h>
+#include <algorithm>
+#include <cctype>
#include <android_audio_policy_configuration_V7_0.h>
@@ -210,6 +212,50 @@
return isOutputDevice(stringToAudioDevice(device));
}
+static inline bool isVendorExtension(const std::string& s) {
+ // Must match the "vendorExtension" rule from the XSD file.
+ static const std::string vendorPrefix = "VX_";
+ return s.size() > vendorPrefix.size() && s.substr(0, vendorPrefix.size()) == vendorPrefix &&
+ std::all_of(s.begin() + vendorPrefix.size(), s.end(),
+ [](unsigned char c) { return c == '_' || std::isalnum(c); });
+}
+
+static inline bool isUnknownAudioChannelMask(const std::string& mask) {
+ return stringToAudioChannelMask(mask) == AudioChannelMask::UNKNOWN;
+}
+
+static inline bool isUnknownAudioContentType(const std::string& contentType) {
+ return stringToAudioContentType(contentType) == AudioContentType::UNKNOWN;
+}
+
+static inline bool isUnknownAudioDevice(const std::string& device) {
+ return stringToAudioDevice(device) == AudioDevice::UNKNOWN && !isVendorExtension(device);
+}
+
+static inline bool isUnknownAudioFormat(const std::string& format) {
+ return stringToAudioFormat(format) == AudioFormat::UNKNOWN && !isVendorExtension(format);
+}
+
+static inline bool isUnknownAudioGainMode(const std::string& mode) {
+ return stringToAudioGainMode(mode) == AudioGainMode::UNKNOWN;
+}
+
+static inline bool isUnknownAudioInOutFlag(const std::string& flag) {
+ return stringToAudioInOutFlag(flag) == AudioInOutFlag::UNKNOWN;
+}
+
+static inline bool isUnknownAudioSource(const std::string& source) {
+ return stringToAudioSource(source) == AudioSource::UNKNOWN;
+}
+
+static inline bool isUnknownAudioStreamType(const std::string& streamType) {
+ return stringToAudioStreamType(streamType) == AudioStreamType::UNKNOWN;
+}
+
+static inline bool isUnknownAudioUsage(const std::string& usage) {
+ return stringToAudioUsage(usage) == AudioUsage::UNKNOWN;
+}
+
} // namespace android::audio::policy::configuration::V7_0
#endif // ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
diff --git a/audio/common/7.0/example/Effect.cpp b/audio/common/7.0/example/Effect.cpp
index 9d5ab31..27f28c6 100644
--- a/audio/common/7.0/example/Effect.cpp
+++ b/audio/common/7.0/example/Effect.cpp
@@ -107,14 +107,14 @@
}
Return<void> Effect::getConfig(getConfig_cb _hidl_cb) {
- const EffectConfig config = {{} /* inputCfg */,
- // outputCfg
- {{} /* buffer */,
- 48000 /* samplingRateHz */,
- toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
- toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT),
- EffectBufferAccess::ACCESS_ACCUMULATE,
- 0 /* mask */}};
+ const EffectConfig config = {
+ {} /* inputCfg */,
+ // outputCfg
+ {{} /* buffer */,
+ {toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT), 48000 /* samplingRateHz */,
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO)}, /* base */
+ EffectBufferAccess::ACCESS_ACCUMULATE,
+ 0 /* mask */}};
_hidl_cb(Result::OK, config);
return Void();
}
diff --git a/audio/common/7.0/types.hal b/audio/common/7.0/types.hal
index 631d524..ed6d94f 100644
--- a/audio/common/7.0/types.hal
+++ b/audio/common/7.0/types.hal
@@ -118,9 +118,9 @@
* Base configuration attributes applicable to any stream of audio.
*/
struct AudioConfigBase {
- AudioFormat format; // 'DEFAULT' means 'unspecified'
+ AudioFormat format; // empty means 'unspecified'
uint32_t sampleRateHz; // 0 means 'unspecified'
- vec<AudioChannelMask> channelMask; // empty means 'unspecified'
+ AudioChannelMask channelMask; // empty means 'unspecified'
};
/**
@@ -167,10 +167,18 @@
AudioDevice deviceType;
safe_union Address {
/**
- * The address may be left unspecified if 'device' specifies
- * a physical device unambiguously.
+ * String uniquely identifying the device among other devices
+ * of the same type. Can be empty in case there is only one device
+ * of this type.
+ *
+ * Depending on the device type, its id may be assigned by the framework
+ * (this is done for REMOTE_SUBMIX), or specified in the audio policy
+ * configuration file (typically done for BUS devices), or assigned
+ * by the HAL service. In any case, both framework and HAL must
+ * never attempt to parse the value of the id. If the address must
+ * be parsed, one of the members below must be used instead of 'id'.
*/
- Monostate unspecified;
+ string id;
/** IEEE 802 MAC address. Set for Bluetooth devices. */
uint8_t[6] mac;
/** IPv4 Address. Set for IPv4 devices. */
@@ -182,10 +190,6 @@
int32_t card;
int32_t device;
} alsa;
- /** Arbitrary BUS device unique address. Not interpreted by the framework. */
- string bus;
- /** Arbitrary REMOTE_SUBMIX device unique address. Not interpreted by the HAL. */
- string rSubmix;
} address;
};
@@ -266,14 +270,29 @@
*/
struct AudioConfig {
AudioConfigBase base;
- AudioOffloadInfo offloadInfo;
+ safe_union OffloadInfo {
+ Monostate unspecified;
+ AudioOffloadInfo info;
+ } offloadInfo;
uint64_t frameCount;
};
+/**
+ * AudioTag is an additional use case qualifier complementing
+ * AudioUsage and AudioContentType. Tags are set by vendor specific applications
+ * and must be prefixed by "VX_". Vendor must namespace their tag
+ * names to avoid conflicts. See 'vendorExtension' in audio_policy_configuration.xsd
+ * for a formal definition.
+ */
+typedef string AudioTag;
+
/** Metadata of a playback track for a StreamOut. */
struct PlaybackTrackMetadata {
AudioUsage usage;
AudioContentType contentType;
+ /** Tags from AudioTrack audio atttributes */
+ vec<AudioTag> tags;
+ AudioChannelMask channelMask;
/**
* Positive linear gain applied to the track samples. 0 being muted and 1 is no attenuation,
* 2 means double amplification...
@@ -290,6 +309,9 @@
/** Metadata of a record track for a StreamIn. */
struct RecordTrackMetadata {
AudioSource source;
+ /** Tags from AudioTrack audio atttributes */
+ vec<AudioTag> tags;
+ AudioChannelMask channelMask;
/**
* Positive linear gain applied to the track samples. 0 being muted and 1 is no attenuation,
* 2 means double amplification...
@@ -328,14 +350,22 @@
* A gain stage is always attached to an audio port.
*/
struct AudioGain {
- vec<AudioGainMode> mode; // modes of operation
- AudioChannelMask channelMask; // channels which gain can be controlled
- int32_t minValue; // minimum gain value in millibels
- int32_t maxValue; // maximum gain value in millibels
- int32_t defaultValue; // default gain value in millibels
- uint32_t stepValue; // gain step in millibels
- uint32_t minRampMs; // minimum ramp duration in ms
- uint32_t maxRampMs; // maximum ramp duration in ms
+ /** Modes of operation. */
+ vec<AudioGainMode> mode;
+ /** Channels which gain can be controlled. */
+ AudioChannelMask channelMask;
+ /** Minimum gain value in millibels. */
+ int32_t minValue;
+ /** Maximum gain value in millibels. */
+ int32_t maxValue;
+ /** Default gain value in millibels. */
+ int32_t defaultValue;
+ /** Gain step in millibels. */
+ uint32_t stepValue;
+ /** Ramp duration in ms. */
+ uint32_t minRampMs;
+ /** Maximum ramp duration in ms. */
+ uint32_t maxRampMs;
};
/**
@@ -343,16 +373,20 @@
* given port.
*/
struct AudioGainConfig {
- int32_t index; // index of the corresponding AudioGain in AudioPort.gains
- vec<AudioGainMode> mode; // modes of operation
- AudioChannelMask channelMask; // channels which gain value follows
+ /** Index of the corresponding AudioGain in AudioPort.gains. */
+ int32_t index;
+ /** Modes of operation. */
+ vec<AudioGainMode> mode;
+ /** Channels which gain value follows. */
+ AudioChannelMask channelMask;
/**
* Gain values in millibels for each channel ordered from LSb to MSb in
* channel mask. The number of values is 1 in joint mode or
- * popcount(channel_mask).
+ * the number of channels in the channel mask.
*/
- int32_t[4 * 8] values;
- uint32_t rampDurationMs; // ramp duration in ms
+ vec<int32_t> values;
+ /** Ramp duration in ms. */
+ uint32_t rampDurationMs;
};
@@ -409,7 +443,7 @@
* parameters (or none) may be set. See the documentation of the
* AudioConfigBase struct.
*/
- AudioConfigBase config;
+ AudioConfigBase base;
/** Associated gain control. */
safe_union OptionalGain {
Monostate unspecified;
@@ -439,6 +473,8 @@
vec<AudioProfile> profiles;
/** List of gain controls attached to the port. */
vec<AudioGain> gains;
+ /** Parameters that depend on the actual port role. */
+ AudioPortExtendedInfo ext;
/**
* Current configuration of the audio port, may have all the fields left
* unspecified.
diff --git a/audio/common/all-versions/default/7.0/HidlUtils.cpp b/audio/common/all-versions/default/7.0/HidlUtils.cpp
new file mode 100644
index 0000000..de19faf
--- /dev/null
+++ b/audio/common/all-versions/default/7.0/HidlUtils.cpp
@@ -0,0 +1,957 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#define LOG_TAG "HidlUtils"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <common/all-versions/HidlSupport.h>
+#include <common/all-versions/VersionUtils.h>
+
+#include "HidlUtils.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+#define CONVERT_CHECKED(expr, result) \
+ if (status_t status = (expr); status != NO_ERROR) { \
+ result = status; \
+ }
+
+status_t HidlUtils::audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+ AudioChannelMask* channelMask) {
+ *channelMask = audio_channel_index_mask_to_string(halChannelMask);
+ if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown index channel mask value 0x%X", halChannelMask);
+ *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioInputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+ AudioChannelMask* channelMask) {
+ *channelMask = audio_channel_in_mask_to_string(halChannelMask);
+ if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown input channel mask value 0x%X", halChannelMask);
+ *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioOutputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+ AudioChannelMask* channelMask) {
+ *channelMask = audio_channel_out_mask_to_string(halChannelMask);
+ if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown output channel mask value 0x%X", halChannelMask);
+ *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+ AudioChannelMask* channelMask) {
+ if (halChannelMask != AUDIO_CHANNEL_NONE) {
+ if (audio_channel_mask_is_valid(halChannelMask)) {
+ switch (audio_channel_mask_get_representation(halChannelMask)) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ return isInput ? audioInputChannelMaskFromHal(halChannelMask, channelMask)
+ : audioOutputChannelMaskFromHal(halChannelMask, channelMask);
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ // Index masks do not have direction.
+ return audioIndexChannelMaskFromHal(halChannelMask, channelMask);
+ // no default
+ }
+ }
+ *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+ return BAD_VALUE;
+ }
+ *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+ return NO_ERROR;
+}
+
+status_t HidlUtils::audioChannelMasksFromHal(const std::vector<std::string>& halChannelMasks,
+ hidl_vec<AudioChannelMask>* channelMasks) {
+ hidl_vec<AudioChannelMask> tempChannelMasks;
+ tempChannelMasks.resize(halChannelMasks.size());
+ size_t tempPos = 0;
+ for (const auto& halChannelMask : halChannelMasks) {
+ if (!halChannelMask.empty() && !xsd::isUnknownAudioChannelMask(halChannelMask)) {
+ tempChannelMasks[tempPos++] = halChannelMask;
+ }
+ }
+ if (tempPos == tempChannelMasks.size()) {
+ *channelMasks = std::move(tempChannelMasks);
+ } else {
+ *channelMasks = hidl_vec<AudioChannelMask>(tempChannelMasks.begin(),
+ tempChannelMasks.begin() + tempPos);
+ }
+ return halChannelMasks.size() == channelMasks->size() ? NO_ERROR : BAD_VALUE;
+}
+
+status_t HidlUtils::audioChannelMaskToHal(const AudioChannelMask& channelMask,
+ audio_channel_mask_t* halChannelMask) {
+ if (!xsd::isUnknownAudioChannelMask(channelMask) &&
+ audio_channel_mask_from_string(channelMask.c_str(), halChannelMask)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown channel mask \"%s\"", channelMask.c_str());
+ *halChannelMask = AUDIO_CHANNEL_NONE;
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioConfigBaseFromHal(const audio_config_base_t& halConfigBase, bool isInput,
+ AudioConfigBase* configBase) {
+ status_t result = NO_ERROR;
+ configBase->sampleRateHz = halConfigBase.sample_rate;
+ CONVERT_CHECKED(
+ audioChannelMaskFromHal(halConfigBase.channel_mask, isInput, &configBase->channelMask),
+ result);
+ CONVERT_CHECKED(audioFormatFromHal(halConfigBase.format, &configBase->format), result);
+ return result;
+}
+
+status_t HidlUtils::audioConfigBaseToHal(const AudioConfigBase& configBase,
+ audio_config_base_t* halConfigBase) {
+ status_t result = NO_ERROR;
+ halConfigBase->sample_rate = configBase.sampleRateHz;
+ CONVERT_CHECKED(audioChannelMaskToHal(configBase.channelMask, &halConfigBase->channel_mask),
+ result);
+ CONVERT_CHECKED(audioFormatToHal(configBase.format, &halConfigBase->format), result);
+ return result;
+}
+
+status_t HidlUtils::audioContentTypeFromHal(const audio_content_type_t halContentType,
+ AudioContentType* contentType) {
+ *contentType = audio_content_type_to_string(halContentType);
+ if (!contentType->empty() && !xsd::isUnknownAudioContentType(*contentType)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio content type value 0x%X", halContentType);
+ *contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioContentTypeToHal(const AudioContentType& contentType,
+ audio_content_type_t* halContentType) {
+ if (!xsd::isUnknownAudioContentType(contentType) &&
+ audio_content_type_from_string(contentType.c_str(), halContentType)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio content type \"%s\"", contentType.c_str());
+ *halContentType = AUDIO_CONTENT_TYPE_UNKNOWN;
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioDeviceTypeFromHal(audio_devices_t halDevice, AudioDevice* device) {
+ *device = audio_device_to_string(halDevice);
+ if (!device->empty() && !xsd::isUnknownAudioDevice(*device)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio device value 0x%X", halDevice);
+ *device = toString(xsd::AudioDevice::AUDIO_DEVICE_NONE);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioDeviceTypeToHal(const AudioDevice& device, audio_devices_t* halDevice) {
+ if (!xsd::isUnknownAudioDevice(device) && audio_device_from_string(device.c_str(), halDevice)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio device \"%s\"", device.c_str());
+ *halDevice = AUDIO_DEVICE_NONE;
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatFromHal(audio_format_t halFormat, AudioFormat* format) {
+ *format = audio_format_to_string(halFormat);
+ if (!format->empty() && !xsd::isUnknownAudioFormat(*format)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio format value 0x%X", halFormat);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatsFromHal(const std::vector<std::string>& halFormats,
+ hidl_vec<AudioFormat>* formats) {
+ hidl_vec<AudioFormat> tempFormats;
+ tempFormats.resize(halFormats.size());
+ size_t tempPos = 0;
+ for (const auto& halFormat : halFormats) {
+ if (!halFormat.empty() && !xsd::isUnknownAudioFormat(halFormat)) {
+ tempFormats[tempPos++] = halFormat;
+ }
+ }
+ if (tempPos == tempFormats.size()) {
+ *formats = std::move(tempFormats);
+ } else {
+ *formats = hidl_vec<AudioFormat>(tempFormats.begin(), tempFormats.begin() + tempPos);
+ }
+ return halFormats.size() == formats->size() ? NO_ERROR : BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatToHal(const AudioFormat& format, audio_format_t* halFormat) {
+ if (!xsd::isUnknownAudioFormat(format) && audio_format_from_string(format.c_str(), halFormat)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio format \"%s\"", format.c_str());
+ *halFormat = AUDIO_FORMAT_DEFAULT;
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioGainModeMaskFromHal(audio_gain_mode_t halGainModeMask,
+ hidl_vec<AudioGainMode>* gainModeMask) {
+ status_t status = NO_ERROR;
+ std::vector<AudioGainMode> result;
+ for (uint32_t bit = 0; bit < sizeof(audio_gain_mode_t) * 8; ++bit) {
+ audio_gain_mode_t flag = static_cast<audio_gain_mode_t>(1u << bit);
+ if ((flag & halGainModeMask) == flag) {
+ AudioGainMode flagStr = audio_gain_mode_to_string(flag);
+ if (!flagStr.empty() && !xsd::isUnknownAudioGainMode(flagStr)) {
+ result.push_back(flagStr);
+ } else {
+ ALOGE("Unknown audio gain mode value 0x%X", flag);
+ status = BAD_VALUE;
+ }
+ }
+ }
+ *gainModeMask = result;
+ return status;
+}
+
+status_t HidlUtils::audioGainModeMaskToHal(const hidl_vec<AudioGainMode>& gainModeMask,
+ audio_gain_mode_t* halGainModeMask) {
+ status_t status = NO_ERROR;
+ *halGainModeMask = {};
+ for (const auto& gainMode : gainModeMask) {
+ audio_gain_mode_t halGainMode;
+ if (!xsd::isUnknownAudioGainMode(gainMode) &&
+ audio_gain_mode_from_string(gainMode.c_str(), &halGainMode)) {
+ *halGainModeMask = static_cast<audio_gain_mode_t>(*halGainModeMask | halGainMode);
+ } else {
+ ALOGE("Unknown audio gain mode \"%s\"", gainMode.c_str());
+ status = BAD_VALUE;
+ }
+ }
+ return status;
+}
+
+status_t HidlUtils::audioSourceFromHal(audio_source_t halSource, AudioSource* source) {
+ *source = audio_source_to_string(halSource);
+ if (!source->empty() && !xsd::isUnknownAudioSource(*source)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio source value 0x%X", halSource);
+ *source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioSourceToHal(const AudioSource& source, audio_source_t* halSource) {
+ if (!xsd::isUnknownAudioSource(source) && audio_source_from_string(source.c_str(), halSource)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio source \"%s\"", source.c_str());
+ *halSource = AUDIO_SOURCE_DEFAULT;
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioStreamTypeFromHal(audio_stream_type_t halStreamType,
+ AudioStreamType* streamType) {
+ *streamType = audio_stream_type_to_string(halStreamType);
+ if (!streamType->empty() && !xsd::isUnknownAudioStreamType(*streamType)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio stream type value 0x%X", halStreamType);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioStreamTypeToHal(const AudioStreamType& streamType,
+ audio_stream_type_t* halStreamType) {
+ if (!xsd::isUnknownAudioStreamType(streamType) &&
+ audio_stream_type_from_string(streamType.c_str(), halStreamType)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio stream type \"%s\"", streamType.c_str());
+ *halStreamType = AUDIO_STREAM_DEFAULT;
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+ AudioConfig* config) {
+ status_t result = NO_ERROR;
+ audio_config_base_t halConfigBase = {halConfig.sample_rate, halConfig.channel_mask,
+ halConfig.format};
+ CONVERT_CHECKED(audioConfigBaseFromHal(halConfigBase, isInput, &config->base), result);
+ if (halConfig.offload_info.sample_rate != 0) {
+ config->offloadInfo.info({});
+ CONVERT_CHECKED(
+ audioOffloadInfoFromHal(halConfig.offload_info, &config->offloadInfo.info()),
+ result);
+ }
+ config->frameCount = halConfig.frame_count;
+ return result;
+}
+
+status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
+ status_t result = NO_ERROR;
+ *halConfig = AUDIO_CONFIG_INITIALIZER;
+ audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+ CONVERT_CHECKED(audioConfigBaseToHal(config.base, &halConfigBase), result);
+ halConfig->sample_rate = halConfigBase.sample_rate;
+ halConfig->channel_mask = halConfigBase.channel_mask;
+ halConfig->format = halConfigBase.format;
+ if (config.offloadInfo.getDiscriminator() ==
+ AudioConfig::OffloadInfo::hidl_discriminator::info) {
+ CONVERT_CHECKED(audioOffloadInfoToHal(config.offloadInfo.info(), &halConfig->offload_info),
+ result);
+ }
+ halConfig->frame_count = config.frameCount;
+ return result;
+}
+
+status_t HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+ AudioGainConfig* config) {
+ status_t result = NO_ERROR;
+ config->index = halConfig.index;
+ CONVERT_CHECKED(audioGainModeMaskFromHal(halConfig.mode, &config->mode), result);
+ CONVERT_CHECKED(audioChannelMaskFromHal(halConfig.channel_mask, isInput, &config->channelMask),
+ result);
+ if (halConfig.mode & AUDIO_GAIN_MODE_JOINT) {
+ config->values.resize(1);
+ config->values[0] = halConfig.values[0];
+ }
+ if (halConfig.mode & (AUDIO_GAIN_MODE_CHANNELS | AUDIO_GAIN_MODE_RAMP)) {
+ config->values.resize(__builtin_popcount(halConfig.channel_mask));
+ for (size_t i = 0; i < config->values.size(); ++i) {
+ config->values[i] = halConfig.values[i];
+ }
+ }
+ config->rampDurationMs = halConfig.ramp_duration_ms;
+ return result;
+}
+
+status_t HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
+ struct audio_gain_config* halConfig) {
+ status_t result = NO_ERROR;
+ halConfig->index = config.index;
+ CONVERT_CHECKED(audioGainModeMaskToHal(config.mode, &halConfig->mode), result);
+ CONVERT_CHECKED(audioChannelMaskToHal(config.channelMask, &halConfig->channel_mask), result);
+ memset(halConfig->values, 0, sizeof(halConfig->values));
+ if (halConfig->mode & AUDIO_GAIN_MODE_JOINT) {
+ if (config.values.size() > 0) {
+ halConfig->values[0] = config.values[0];
+ } else {
+ ALOGE("Empty values vector in AudioGainConfig");
+ result = BAD_VALUE;
+ }
+ }
+ if (halConfig->mode & (AUDIO_GAIN_MODE_CHANNELS | AUDIO_GAIN_MODE_RAMP)) {
+ size_t channelCount = __builtin_popcount(halConfig->channel_mask);
+ size_t valuesCount = config.values.size();
+ if (channelCount != valuesCount) {
+ ALOGE("Wrong number of values in AudioGainConfig, expected: %zu, found: %zu",
+ channelCount, valuesCount);
+ result = BAD_VALUE;
+ if (channelCount < valuesCount) {
+ valuesCount = channelCount;
+ }
+ }
+ for (size_t i = 0; i < valuesCount; ++i) {
+ halConfig->values[i] = config.values[i];
+ }
+ }
+ halConfig->ramp_duration_ms = config.rampDurationMs;
+ return result;
+}
+
+status_t HidlUtils::audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+ AudioGain* gain) {
+ status_t result = NO_ERROR;
+ CONVERT_CHECKED(audioGainModeMaskFromHal(halGain.mode, &gain->mode), result);
+ CONVERT_CHECKED(audioChannelMaskFromHal(halGain.channel_mask, isInput, &gain->channelMask),
+ result);
+ gain->minValue = halGain.min_value;
+ gain->maxValue = halGain.max_value;
+ gain->defaultValue = halGain.default_value;
+ gain->stepValue = halGain.step_value;
+ gain->minRampMs = halGain.min_ramp_ms;
+ gain->maxRampMs = halGain.max_ramp_ms;
+ return result;
+}
+
+status_t HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
+ status_t result = NO_ERROR;
+ CONVERT_CHECKED(audioGainModeMaskToHal(gain.mode, &halGain->mode), result);
+ CONVERT_CHECKED(audioChannelMaskToHal(gain.channelMask, &halGain->channel_mask), result);
+ halGain->min_value = gain.minValue;
+ halGain->max_value = gain.maxValue;
+ halGain->default_value = gain.defaultValue;
+ halGain->step_value = gain.stepValue;
+ halGain->min_ramp_ms = gain.minRampMs;
+ halGain->max_ramp_ms = gain.maxRampMs;
+ return result;
+}
+
+status_t HidlUtils::audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage) {
+ if (halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST ||
+ halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT ||
+ halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED ||
+ halUsage == AUDIO_USAGE_NOTIFICATION_EVENT) {
+ halUsage = AUDIO_USAGE_NOTIFICATION;
+ }
+ *usage = audio_usage_to_string(halUsage);
+ if (!usage->empty() && !xsd::isUnknownAudioUsage(*usage)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio usage %d", halUsage);
+ *usage = toString(xsd::AudioUsage::AUDIO_USAGE_UNKNOWN);
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage) {
+ if (!xsd::isUnknownAudioUsage(usage) && audio_usage_from_string(usage.c_str(), halUsage)) {
+ return NO_ERROR;
+ }
+ ALOGE("Unknown audio usage \"%s\"", usage.c_str());
+ *halUsage = AUDIO_USAGE_UNKNOWN;
+ return BAD_VALUE;
+}
+
+status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
+ AudioOffloadInfo* offload) {
+ status_t result = NO_ERROR;
+ audio_config_base_t halConfigBase = {halOffload.sample_rate, halOffload.channel_mask,
+ halOffload.format};
+ CONVERT_CHECKED(audioConfigBaseFromHal(halConfigBase, false /*isInput*/, &offload->base),
+ result);
+ CONVERT_CHECKED(audioStreamTypeFromHal(halOffload.stream_type, &offload->streamType), result);
+ offload->bitRatePerSecond = halOffload.bit_rate;
+ offload->durationMicroseconds = halOffload.duration_us;
+ offload->hasVideo = halOffload.has_video;
+ offload->isStreaming = halOffload.is_streaming;
+ offload->bitWidth = halOffload.bit_width;
+ offload->bufferSize = halOffload.offload_buffer_size;
+ CONVERT_CHECKED(audioUsageFromHal(halOffload.usage, &offload->usage), result);
+ if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
+ offload->encapsulationMode =
+ static_cast<AudioEncapsulationMode>(halOffload.encapsulation_mode);
+ offload->contentId = halOffload.content_id;
+ offload->syncId = halOffload.sync_id;
+ } else {
+ offload->encapsulationMode = AudioEncapsulationMode::NONE;
+ offload->contentId = 0;
+ offload->syncId = 0;
+ }
+ return result;
+}
+
+status_t HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+ audio_offload_info_t* halOffload) {
+ status_t result = NO_ERROR;
+ *halOffload = AUDIO_INFO_INITIALIZER;
+ audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+ CONVERT_CHECKED(audioConfigBaseToHal(offload.base, &halConfigBase), result);
+ halOffload->sample_rate = halConfigBase.sample_rate;
+ halOffload->channel_mask = halConfigBase.channel_mask;
+ halOffload->format = halConfigBase.format;
+ CONVERT_CHECKED(audioStreamTypeToHal(offload.streamType, &halOffload->stream_type), result);
+ halOffload->bit_rate = offload.bitRatePerSecond;
+ halOffload->duration_us = offload.durationMicroseconds;
+ halOffload->has_video = offload.hasVideo;
+ halOffload->is_streaming = offload.isStreaming;
+ halOffload->bit_width = offload.bitWidth;
+ halOffload->offload_buffer_size = offload.bufferSize;
+ CONVERT_CHECKED(audioUsageToHal(offload.usage, &halOffload->usage), result);
+ halOffload->encapsulation_mode =
+ static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
+ halOffload->content_id = offload.contentId;
+ halOffload->sync_id = offload.syncId;
+ return result;
+}
+
+status_t HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
+ AudioPortConfig* config) {
+ status_t result = NO_ERROR;
+ bool isInput = false;
+ config->id = halConfig.id;
+ CONVERT_CHECKED(audioPortExtendedInfoFromHal(halConfig.role, halConfig.type,
+ halConfig.ext.device, halConfig.ext.mix,
+ halConfig.ext.session, &config->ext, &isInput),
+ result);
+ if (audio_port_config_has_input_direction(&halConfig) != isInput) {
+ ALOGE("Inconsistent port config direction data, is input: %d (hal) != %d (converter)",
+ audio_port_config_has_input_direction(&halConfig), isInput);
+ result = BAD_VALUE;
+ }
+ if (halConfig.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ config->base.sampleRateHz = halConfig.sample_rate;
+ } else {
+ config->base.sampleRateHz = {};
+ }
+ if (halConfig.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ CONVERT_CHECKED(
+ audioChannelMaskFromHal(halConfig.channel_mask, isInput, &config->base.channelMask),
+ result);
+ } else {
+ config->base.channelMask = {};
+ }
+ if (halConfig.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ CONVERT_CHECKED(audioFormatFromHal(halConfig.format, &config->base.format), result);
+ } else {
+ config->base.format = {};
+ }
+ if (halConfig.config_mask & AUDIO_PORT_CONFIG_GAIN) {
+ config->gain.config({});
+ CONVERT_CHECKED(audioGainConfigFromHal(halConfig.gain, isInput, &config->gain.config()),
+ result);
+ } else {
+ config->gain.unspecified({});
+ }
+ return result;
+}
+
+status_t HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
+ struct audio_port_config* halConfig) {
+ status_t result = NO_ERROR;
+ memset(halConfig, 0, sizeof(audio_port_config));
+ halConfig->id = config.id;
+ halConfig->config_mask = {};
+ if (config.base.sampleRateHz != 0) {
+ halConfig->config_mask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
+ halConfig->sample_rate = config.base.sampleRateHz;
+ }
+ if (!config.base.channelMask.empty()) {
+ halConfig->config_mask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
+ CONVERT_CHECKED(audioChannelMaskToHal(config.base.channelMask, &halConfig->channel_mask),
+ result);
+ }
+ if (!config.base.format.empty()) {
+ halConfig->config_mask |= AUDIO_PORT_CONFIG_FORMAT;
+ CONVERT_CHECKED(audioFormatToHal(config.base.format, &halConfig->format), result);
+ }
+ if (config.gain.getDiscriminator() ==
+ AudioPortConfig::OptionalGain::hidl_discriminator::config) {
+ halConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+ CONVERT_CHECKED(audioGainConfigToHal(config.gain.config(), &halConfig->gain), result);
+ }
+ CONVERT_CHECKED(audioPortExtendedInfoToHal(config.ext, &halConfig->role, &halConfig->type,
+ &halConfig->ext.device, &halConfig->ext.mix,
+ &halConfig->ext.session),
+ result);
+ return result;
+}
+
+status_t HidlUtils::audioPortExtendedInfoFromHal(
+ audio_port_role_t role, audio_port_type_t type,
+ const struct audio_port_config_device_ext& device,
+ const struct audio_port_config_mix_ext& mix,
+ const struct audio_port_config_session_ext& session, AudioPortExtendedInfo* ext,
+ bool* isInput) {
+ status_t result = NO_ERROR;
+ *isInput = false;
+ switch (type) {
+ case AUDIO_PORT_TYPE_NONE:
+ ext->unspecified({});
+ break;
+ case AUDIO_PORT_TYPE_DEVICE: {
+ *isInput = role == AUDIO_PORT_ROLE_SOURCE;
+ ext->device({});
+ CONVERT_CHECKED(deviceAddressFromHal(device.type, device.address, &ext->device()),
+ result);
+ break;
+ }
+ case AUDIO_PORT_TYPE_MIX: {
+ *isInput = role == AUDIO_PORT_ROLE_SINK;
+ ext->mix({});
+ ext->mix().ioHandle = mix.handle;
+ if (role == AUDIO_PORT_ROLE_SOURCE) {
+ ext->mix().useCase.stream({});
+ CONVERT_CHECKED(
+ audioStreamTypeFromHal(mix.usecase.stream, &ext->mix().useCase.stream()),
+ result);
+ } else if (role == AUDIO_PORT_ROLE_SINK) {
+ ext->mix().useCase.source({});
+ CONVERT_CHECKED(
+ audioSourceFromHal(mix.usecase.source, &ext->mix().useCase.source()),
+ result);
+ }
+ break;
+ }
+ case AUDIO_PORT_TYPE_SESSION: {
+ ext->session(session.session);
+ break;
+ }
+ }
+ return result;
+}
+
+status_t HidlUtils::audioPortExtendedInfoToHal(const AudioPortExtendedInfo& ext,
+ audio_port_role_t* role, audio_port_type_t* type,
+ struct audio_port_config_device_ext* device,
+ struct audio_port_config_mix_ext* mix,
+ struct audio_port_config_session_ext* session) {
+ status_t result = NO_ERROR;
+ switch (ext.getDiscriminator()) {
+ case AudioPortExtendedInfo::hidl_discriminator::unspecified:
+ *role = AUDIO_PORT_ROLE_NONE;
+ *type = AUDIO_PORT_TYPE_NONE;
+ break;
+ case AudioPortExtendedInfo::hidl_discriminator::device:
+ *role = xsd::isOutputDevice(ext.device().deviceType) ? AUDIO_PORT_ROLE_SINK
+ : AUDIO_PORT_ROLE_SOURCE;
+ *type = AUDIO_PORT_TYPE_DEVICE;
+ CONVERT_CHECKED(deviceAddressToHal(ext.device(), &device->type, device->address),
+ result);
+ break;
+ case AudioPortExtendedInfo::hidl_discriminator::mix:
+ *type = AUDIO_PORT_TYPE_MIX;
+ switch (ext.mix().useCase.getDiscriminator()) {
+ case AudioPortExtendedInfo::AudioPortMixExt::UseCase::hidl_discriminator::stream:
+ *role = AUDIO_PORT_ROLE_SOURCE;
+ CONVERT_CHECKED(
+ audioStreamTypeToHal(ext.mix().useCase.stream(), &mix->usecase.stream),
+ result);
+ break;
+ case AudioPortExtendedInfo::AudioPortMixExt::UseCase::hidl_discriminator::source:
+ *role = AUDIO_PORT_ROLE_SINK;
+ CONVERT_CHECKED(
+ audioSourceToHal(ext.mix().useCase.source(), &mix->usecase.source),
+ result);
+ break;
+ }
+ mix->handle = ext.mix().ioHandle;
+ break;
+ case AudioPortExtendedInfo::hidl_discriminator::session:
+ *role = AUDIO_PORT_ROLE_NONE;
+ *type = AUDIO_PORT_TYPE_SESSION;
+ session->session = static_cast<audio_session_t>(ext.session());
+ break;
+ }
+ return result;
+}
+
+status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
+ struct audio_port_v7 halPortV7 = {};
+ audio_populate_audio_port_v7(&halPort, &halPortV7);
+ return audioPortFromHal(halPortV7, port);
+}
+
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
+ status_t result = NO_ERROR;
+ struct audio_port_v7 halPortV7 = {};
+ CONVERT_CHECKED(audioPortToHal(port, &halPortV7), result);
+ if (!audio_populate_audio_port(&halPortV7, halPort)) {
+ result = BAD_VALUE;
+ }
+ return result;
+}
+
+status_t HidlUtils::audioPortFromHal(const struct audio_port_v7& halPort, AudioPort* port) {
+ status_t result = NO_ERROR;
+ bool isInput = false;
+ port->id = halPort.id;
+ port->name.setToExternal(halPort.name, strlen(halPort.name));
+ // HAL uses slightly different but convertible structures for the extended info in port
+ // and port config structures.
+ struct audio_port_config_device_ext halDevice = {};
+ struct audio_port_config_mix_ext halMix = {};
+ struct audio_port_config_session_ext halSession = {};
+ switch (halPort.type) {
+ case AUDIO_PORT_TYPE_NONE:
+ break;
+ case AUDIO_PORT_TYPE_DEVICE:
+ halDevice.type = halPort.ext.device.type;
+ memcpy(halDevice.address, halPort.ext.device.address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+ break;
+ case AUDIO_PORT_TYPE_MIX:
+ halMix.handle = halPort.ext.mix.handle;
+ break;
+ case AUDIO_PORT_TYPE_SESSION:
+ halSession.session = halPort.ext.session.session;
+ break;
+ }
+ 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);
+ }
+ 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);
+ }
+ CONVERT_CHECKED(audioPortConfigFromHal(halPort.active_config, &port->activeConfig), result);
+ return result;
+}
+
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port_v7* halPort) {
+ status_t result = NO_ERROR;
+ halPort->id = port.id;
+ strncpy(halPort->name, port.name.c_str(), AUDIO_PORT_MAX_NAME_LEN);
+ halPort->name[AUDIO_PORT_MAX_NAME_LEN - 1] = '\0';
+ if (port.name.size() >= AUDIO_PORT_MAX_NAME_LEN) {
+ 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);
+ }
+ 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);
+ halPort->num_gains = AUDIO_PORT_MAX_GAINS;
+ result = BAD_VALUE;
+ }
+ for (size_t i = 0; i < halPort->num_gains; ++i) {
+ CONVERT_CHECKED(audioGainToHal(port.gains[i], &halPort->gains[i]), result);
+ }
+ // HAL uses slightly different but convertible structures for the extended info in port
+ // and port config structures.
+ struct audio_port_config_device_ext halDevice = {};
+ struct audio_port_config_mix_ext halMix = {};
+ struct audio_port_config_session_ext halSession = {};
+ CONVERT_CHECKED(audioPortExtendedInfoToHal(port.ext, &halPort->role, &halPort->type, &halDevice,
+ &halMix, &halSession),
+ result);
+ switch (halPort->type) {
+ case AUDIO_PORT_TYPE_NONE:
+ break;
+ case AUDIO_PORT_TYPE_DEVICE:
+ halPort->ext.device.type = halDevice.type;
+ memcpy(halPort->ext.device.address, halDevice.address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+ break;
+ case AUDIO_PORT_TYPE_MIX:
+ halPort->ext.mix.handle = halMix.handle;
+ break;
+ case AUDIO_PORT_TYPE_SESSION:
+ halPort->ext.session.session = halSession.session;
+ break;
+ }
+ CONVERT_CHECKED(audioPortConfigToHal(port.activeConfig, &halPort->active_config), result);
+ return result;
+}
+
+status_t HidlUtils::audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
+ AudioProfile* profile) {
+ status_t result = NO_ERROR;
+ CONVERT_CHECKED(audioFormatFromHal(halProfile.format, &profile->format), result);
+ profile->sampleRates.resize(halProfile.num_sample_rates);
+ for (size_t i = 0; i < halProfile.num_sample_rates; ++i) {
+ profile->sampleRates[i] = halProfile.sample_rates[i];
+ }
+ profile->channelMasks.resize(halProfile.num_channel_masks);
+ for (size_t i = 0; i < halProfile.num_channel_masks; ++i) {
+ CONVERT_CHECKED(audioChannelMaskFromHal(halProfile.channel_masks[i], isInput,
+ &profile->channelMasks[i]),
+ result);
+ }
+ return result;
+}
+
+status_t HidlUtils::audioProfileToHal(const AudioProfile& profile,
+ struct audio_profile* halProfile) {
+ status_t result = NO_ERROR;
+ CONVERT_CHECKED(audioFormatToHal(profile.format, &halProfile->format), result);
+ memset(halProfile->sample_rates, 0, sizeof(halProfile->sample_rates));
+ halProfile->num_sample_rates = profile.sampleRates.size();
+ if (halProfile->num_sample_rates > AUDIO_PORT_MAX_SAMPLING_RATES) {
+ ALOGE("HIDL Audio profile has too many sample rates: %u", halProfile->num_sample_rates);
+ halProfile->num_sample_rates = AUDIO_PORT_MAX_SAMPLING_RATES;
+ result = BAD_VALUE;
+ }
+ for (size_t i = 0; i < halProfile->num_sample_rates; ++i) {
+ halProfile->sample_rates[i] = profile.sampleRates[i];
+ }
+ memset(halProfile->channel_masks, 0, sizeof(halProfile->channel_masks));
+ halProfile->num_channel_masks = profile.channelMasks.size();
+ if (halProfile->num_channel_masks > AUDIO_PORT_MAX_CHANNEL_MASKS) {
+ ALOGE("HIDL Audio profile has too many channel masks: %u", halProfile->num_channel_masks);
+ halProfile->num_channel_masks = AUDIO_PORT_MAX_CHANNEL_MASKS;
+ result = BAD_VALUE;
+ }
+ for (size_t i = 0; i < halProfile->num_channel_masks; ++i) {
+ CONVERT_CHECKED(
+ audioChannelMaskToHal(profile.channelMasks[i], &halProfile->channel_masks[i]),
+ status);
+ }
+ return result;
+}
+
+status_t HidlUtils::audioTagsFromHal(const char* halTags, hidl_vec<AudioTag>* tags) {
+ std::vector<std::string> strTags = utils::splitString(halTags, sAudioTagSeparator);
+ status_t result = NO_ERROR;
+ tags->resize(strTags.size());
+ size_t to = 0;
+ for (size_t from = 0; from < strTags.size(); ++from) {
+ if (xsd::isVendorExtension(strTags[from])) {
+ (*tags)[to++] = strTags[from];
+ } else {
+ result = BAD_VALUE;
+ }
+ }
+ if (to != strTags.size()) {
+ tags->resize(to);
+ }
+ return result;
+}
+
+status_t HidlUtils::audioTagsToHal(const hidl_vec<AudioTag>& tags, char* halTags) {
+ memset(halTags, 0, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
+ status_t result = NO_ERROR;
+ std::ostringstream halTagsBuffer;
+ bool hasValue = false;
+ for (const auto& tag : tags) {
+ if (hasValue) {
+ halTagsBuffer << sAudioTagSeparator;
+ }
+ if (xsd::isVendorExtension(tag) && strchr(tag.c_str(), sAudioTagSeparator) == nullptr) {
+ halTagsBuffer << tag;
+ hasValue = true;
+ } else {
+ result = BAD_VALUE;
+ }
+ }
+ std::string fullHalTags{std::move(halTagsBuffer.str())};
+ strncpy(halTags, fullHalTags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
+ CONVERT_CHECKED(fullHalTags.length() <= AUDIO_ATTRIBUTES_TAGS_MAX_SIZE ? NO_ERROR : BAD_VALUE,
+ result);
+ return result;
+}
+
+status_t HidlUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
+ const char* halDeviceAddress, DeviceAddress* device) {
+ status_t result = NO_ERROR;
+ CONVERT_CHECKED(audioDeviceTypeFromHal(halDeviceType, &device->deviceType), result);
+ if (audio_is_a2dp_out_device(halDeviceType) || audio_is_a2dp_in_device(halDeviceType)) {
+ device->address.mac({});
+ if (halDeviceAddress != nullptr) {
+ auto& mac = device->address.mac();
+ int status = sscanf(halDeviceAddress, "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", &mac[0], &mac[1],
+ &mac[2], &mac[3], &mac[4], &mac[5]);
+ if (status != 6) {
+ ALOGE("BT A2DP device \"%s\" MAC address \"%s\" is invalid",
+ device->deviceType.c_str(), halDeviceAddress);
+ result = BAD_VALUE;
+ }
+ } else {
+ ALOGE("BT A2DP device \"%s\" does not have a MAC address", halDeviceAddress);
+ result = BAD_VALUE;
+ }
+ } else if (halDeviceType == AUDIO_DEVICE_OUT_IP || halDeviceType == AUDIO_DEVICE_IN_IP) {
+ device->address.ipv4({});
+ if (halDeviceAddress != nullptr) {
+ auto& ipv4 = device->address.ipv4();
+ int status = sscanf(halDeviceAddress, "%hhu.%hhu.%hhu.%hhu", &ipv4[0], &ipv4[1],
+ &ipv4[2], &ipv4[3]);
+ if (status != 4) {
+ ALOGE("IP device \"%s\" IPv4 address \"%s\" is invalid", device->deviceType.c_str(),
+ halDeviceAddress);
+ result = BAD_VALUE;
+ }
+ } else {
+ ALOGE("IP device \"%s\" does not have an IPv4 address", device->deviceType.c_str());
+ result = BAD_VALUE;
+ }
+ } else if (audio_is_usb_out_device(halDeviceType) || audio_is_usb_in_device(halDeviceType)) {
+ device->address.alsa({});
+ if (halDeviceAddress != nullptr) {
+ auto& alsa = device->address.alsa();
+ int status = sscanf(halDeviceAddress, "card=%d;device=%d", &alsa.card, &alsa.device);
+ if (status != 2) {
+ ALOGE("USB device \"%s\" ALSA address \"%s\" is invalid",
+ device->deviceType.c_str(), halDeviceAddress);
+ result = BAD_VALUE;
+ }
+ } else {
+ ALOGE("USB device \"%s\" does not have ALSA address", device->deviceType.c_str());
+ result = BAD_VALUE;
+ }
+ } else {
+ // Any other device type uses the 'id' field.
+ device->address.id(halDeviceAddress != nullptr ? halDeviceAddress : "");
+ }
+ return result;
+}
+
+status_t HidlUtils::deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+ char* halDeviceAddress) {
+ status_t result = NO_ERROR;
+ CONVERT_CHECKED(audioDeviceTypeToHal(device.deviceType, halDeviceType), result);
+ memset(halDeviceAddress, 0, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+ if (audio_is_a2dp_out_device(*halDeviceType) || audio_is_a2dp_in_device(*halDeviceType)) {
+ if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::mac) {
+ const auto& mac = device.address.mac();
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN,
+ "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4],
+ mac[5]);
+ } else {
+ ALOGE("BT A2DP device \"%s\" does not have MAC address set", device.deviceType.c_str());
+ result = BAD_VALUE;
+ }
+ } else if (*halDeviceType == AUDIO_DEVICE_OUT_IP || *halDeviceType == AUDIO_DEVICE_IN_IP) {
+ if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::ipv4) {
+ const auto& ipv4 = device.address.ipv4();
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%d.%d.%d.%d", ipv4[0],
+ ipv4[1], ipv4[2], ipv4[3]);
+ } else {
+ ALOGE("IP device \"%s\" does not have IPv4 address set", device.deviceType.c_str());
+ result = BAD_VALUE;
+ }
+ } else if (audio_is_usb_out_device(*halDeviceType) || audio_is_usb_in_device(*halDeviceType)) {
+ if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::alsa) {
+ const auto& alsa = device.address.alsa();
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "card=%d;device=%d", alsa.card,
+ alsa.device);
+ } else {
+ ALOGE("USB device \"%s\" does not have ALSA address set", device.deviceType.c_str());
+ result = BAD_VALUE;
+ }
+ } else {
+ // Any other device type uses the 'id' field.
+ if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::id) {
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s",
+ device.address.id().c_str());
+ }
+ }
+ return result;
+}
+
+} // namespace implementation
+} // namespace CPP_VERSION
+} // namespace common
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/common/all-versions/default/Android.bp b/audio/common/all-versions/default/Android.bp
index a72c8dc..45f0b8f 100644
--- a/audio/common/all-versions/default/Android.bp
+++ b/audio/common/all-versions/default/Android.bp
@@ -39,14 +39,20 @@
],
}
+filegroup {
+ name: "android.hardware.audio.common-util@2-6",
+ srcs: [
+ "HidlUtils.cpp",
+ "HidlUtilsCommon.cpp",
+ "UuidUtils.cpp",
+ ],
+}
+
cc_defaults {
name: "android.hardware.audio.common-util_default",
defaults: ["hidl_defaults"],
vendor_available: true,
- srcs: [
- "HidlUtils.cpp",
- ],
export_include_dirs: ["."],
@@ -69,6 +75,7 @@
cc_library_shared {
name: "android.hardware.audio.common@2.0-util",
defaults: ["android.hardware.audio.common-util_default"],
+ srcs: [":android.hardware.audio.common-util@2-6"],
shared_libs: [
"android.hardware.audio.common@2.0",
],
@@ -82,6 +89,7 @@
cc_library_shared {
name: "android.hardware.audio.common@4.0-util",
defaults: ["android.hardware.audio.common-util_default"],
+ srcs: [":android.hardware.audio.common-util@2-6"],
shared_libs: [
"android.hardware.audio.common@4.0",
],
@@ -95,6 +103,7 @@
cc_library_shared {
name: "android.hardware.audio.common@5.0-util",
defaults: ["android.hardware.audio.common-util_default"],
+ srcs: [":android.hardware.audio.common-util@2-6"],
shared_libs: [
"android.hardware.audio.common@5.0",
],
@@ -108,6 +117,7 @@
cc_library_shared {
name: "android.hardware.audio.common@6.0-util",
defaults: ["android.hardware.audio.common-util_default"],
+ srcs: [":android.hardware.audio.common-util@2-6"],
shared_libs: [
"android.hardware.audio.common@6.0",
],
@@ -118,12 +128,19 @@
],
}
-cc_library_shared {
- enabled: false,
+cc_library {
name: "android.hardware.audio.common@7.0-util",
defaults: ["android.hardware.audio.common-util_default"],
+ srcs: [
+ "7.0/HidlUtils.cpp",
+ "HidlUtilsCommon.cpp",
+ "UuidUtils.cpp",
+ ],
shared_libs: [
"android.hardware.audio.common@7.0",
+ "android.hardware.audio.common@7.0-enums",
+ "libbase",
+ "libxml2",
],
cflags: [
"-DMAJOR_VERSION=7",
@@ -131,3 +148,35 @@
"-include common/all-versions/VersionMacro.h",
],
}
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of conversion utilities.
+cc_test {
+ name: "android.hardware.audio.common@7.0-util_tests",
+ defaults: ["android.hardware.audio.common-util_default"],
+
+ srcs: ["tests/hidlutils_tests.cpp"],
+
+ // Use static linking to allow running in presubmit on
+ // targets that don't have HAL V7.
+ static_libs: [
+ "android.hardware.audio.common@7.0-enums",
+ "android.hardware.audio.common@7.0-util",
+ "android.hardware.audio.common@7.0",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libxml2",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-DMAJOR_VERSION=7",
+ "-DMINOR_VERSION=0",
+ "-include common/all-versions/VersionMacro.h",
+ ],
+
+ test_suites: ["device-tests"],
+}
diff --git a/audio/common/all-versions/default/HidlUtils.cpp b/audio/common/all-versions/default/HidlUtils.cpp
index a470c9c..c0dcd80 100644
--- a/audio/common/all-versions/default/HidlUtils.cpp
+++ b/audio/common/all-versions/default/HidlUtils.cpp
@@ -28,7 +28,7 @@
namespace CPP_VERSION {
namespace implementation {
-status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config) {
+status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, bool, AudioConfig* config) {
config->sampleRateHz = halConfig.sample_rate;
config->channelMask = EnumBitfield<AudioChannelMask>(halConfig.channel_mask);
config->format = AudioFormat(halConfig.format);
@@ -37,17 +37,18 @@
return status;
}
-void HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
+status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
memset(halConfig, 0, sizeof(audio_config_t));
halConfig->sample_rate = config.sampleRateHz;
halConfig->channel_mask = static_cast<audio_channel_mask_t>(config.channelMask);
halConfig->format = static_cast<audio_format_t>(config.format);
audioOffloadInfoToHal(config.offloadInfo, &halConfig->offload_info);
halConfig->frame_count = config.frameCount;
+ return NO_ERROR;
}
-void HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig,
- AudioGainConfig* config) {
+status_t HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool,
+ AudioGainConfig* config) {
config->index = halConfig.index;
config->mode = EnumBitfield<AudioGainMode>(halConfig.mode);
config->channelMask = EnumBitfield<AudioChannelMask>(halConfig.channel_mask);
@@ -55,10 +56,11 @@
config->values[i] = halConfig.values[i];
}
config->rampDurationMs = halConfig.ramp_duration_ms;
+ return NO_ERROR;
}
-void HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
- struct audio_gain_config* halConfig) {
+status_t HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
+ struct audio_gain_config* halConfig) {
halConfig->index = config.index;
halConfig->mode = static_cast<audio_gain_mode_t>(config.mode);
halConfig->channel_mask = static_cast<audio_channel_mask_t>(config.channelMask);
@@ -67,9 +69,10 @@
halConfig->values[i] = config.values[i];
}
halConfig->ramp_duration_ms = config.rampDurationMs;
+ return NO_ERROR;
}
-void HidlUtils::audioGainFromHal(const struct audio_gain& halGain, AudioGain* gain) {
+status_t HidlUtils::audioGainFromHal(const struct audio_gain& halGain, bool, AudioGain* gain) {
gain->mode = EnumBitfield<AudioGainMode>(halGain.mode);
gain->channelMask = EnumBitfield<AudioChannelMask>(halGain.channel_mask);
gain->minValue = halGain.min_value;
@@ -78,9 +81,10 @@
gain->stepValue = halGain.step_value;
gain->minRampMs = halGain.min_ramp_ms;
gain->maxRampMs = halGain.max_ramp_ms;
+ return NO_ERROR;
}
-void HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
+status_t HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
halGain->mode = static_cast<audio_gain_mode_t>(gain.mode);
halGain->channel_mask = static_cast<audio_channel_mask_t>(gain.channelMask);
halGain->min_value = gain.minValue;
@@ -89,22 +93,26 @@
halGain->step_value = gain.stepValue;
halGain->min_ramp_ms = gain.minRampMs;
halGain->max_ramp_ms = gain.maxRampMs;
+ return NO_ERROR;
}
-AudioUsage HidlUtils::audioUsageFromHal(const audio_usage_t halUsage) {
+status_t HidlUtils::audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage) {
switch (halUsage) {
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
case AUDIO_USAGE_NOTIFICATION_EVENT:
- return AudioUsage::NOTIFICATION;
+ *usage = AudioUsage::NOTIFICATION;
+ break;
default:
- return static_cast<AudioUsage>(halUsage);
+ *usage = static_cast<AudioUsage>(halUsage);
}
+ return NO_ERROR;
}
-audio_usage_t HidlUtils::audioUsageToHal(const AudioUsage usage) {
- return static_cast<audio_usage_t>(usage);
+status_t HidlUtils::audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage) {
+ *halUsage = static_cast<audio_usage_t>(usage);
+ return NO_ERROR;
}
status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
@@ -119,7 +127,7 @@
offload->isStreaming = halOffload.is_streaming;
offload->bitWidth = halOffload.bit_width;
offload->bufferSize = halOffload.offload_buffer_size;
- offload->usage = audioUsageFromHal(halOffload.usage);
+ audioUsageFromHal(halOffload.usage, &offload->usage);
#if MAJOR_VERSION >= 6
if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
offload->encapsulationMode =
@@ -139,11 +147,11 @@
return BAD_VALUE;
}
#endif
- return OK;
+ return NO_ERROR;
}
-void HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
- audio_offload_info_t* halOffload) {
+status_t HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+ audio_offload_info_t* halOffload) {
*halOffload = AUDIO_INFO_INITIALIZER;
halOffload->sample_rate = offload.sampleRateHz;
halOffload->channel_mask = static_cast<audio_channel_mask_t>(offload.channelMask);
@@ -155,7 +163,7 @@
halOffload->is_streaming = offload.isStreaming;
halOffload->bit_width = offload.bitWidth;
halOffload->offload_buffer_size = offload.bufferSize;
- halOffload->usage = audioUsageToHal(offload.usage);
+ audioUsageToHal(offload.usage, &halOffload->usage);
#if MAJOR_VERSION >= 6
halOffload->encapsulation_mode =
static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
@@ -164,10 +172,11 @@
#else
// offload doesn't contain encapsulationMode, contentId, syncId, so this is OK.
#endif
+ return NO_ERROR;
}
-void HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
- AudioPortConfig* config) {
+status_t HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
+ AudioPortConfig* config) {
config->id = halConfig.id;
config->role = AudioPortRole(halConfig.role);
config->type = AudioPortType(halConfig.type);
@@ -175,7 +184,7 @@
config->sampleRateHz = halConfig.sample_rate;
config->channelMask = EnumBitfield<AudioChannelMask>(halConfig.channel_mask);
config->format = AudioFormat(halConfig.format);
- audioGainConfigFromHal(halConfig.gain, &config->gain);
+ audioGainConfigFromHal(halConfig.gain, false /*isInput--ignored*/, &config->gain);
switch (halConfig.type) {
case AUDIO_PORT_TYPE_NONE:
break;
@@ -201,10 +210,11 @@
break;
}
}
+ return NO_ERROR;
}
-void HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
- struct audio_port_config* halConfig) {
+status_t HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
+ struct audio_port_config* halConfig) {
memset(halConfig, 0, sizeof(audio_port_config));
halConfig->id = config.id;
halConfig->role = static_cast<audio_port_role_t>(config.role);
@@ -242,27 +252,10 @@
break;
}
}
+ return NO_ERROR;
}
-void HidlUtils::audioPortConfigsFromHal(unsigned int numHalConfigs,
- const struct audio_port_config* halConfigs,
- hidl_vec<AudioPortConfig>* configs) {
- configs->resize(numHalConfigs);
- for (unsigned int i = 0; i < numHalConfigs; ++i) {
- audioPortConfigFromHal(halConfigs[i], &(*configs)[i]);
- }
-}
-
-std::unique_ptr<audio_port_config[]> HidlUtils::audioPortConfigsToHal(
- const hidl_vec<AudioPortConfig>& configs) {
- std::unique_ptr<audio_port_config[]> halConfigs(new audio_port_config[configs.size()]);
- for (size_t i = 0; i < configs.size(); ++i) {
- audioPortConfigToHal(configs[i], &halConfigs[i]);
- }
- return halConfigs;
-}
-
-void HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
+status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
port->id = halPort.id;
port->role = AudioPortRole(halPort.role);
port->type = AudioPortType(halPort.type);
@@ -281,7 +274,7 @@
}
port->gains.resize(halPort.num_gains);
for (size_t i = 0; i < halPort.num_gains; ++i) {
- audioGainFromHal(halPort.gains[i], &port->gains[i]);
+ audioGainFromHal(halPort.gains[i], false /*isInput--ignored*/, &port->gains[i]);
}
audioPortConfigFromHal(halPort.active_config, &port->activeConfig);
switch (halPort.type) {
@@ -305,9 +298,10 @@
break;
}
}
+ return NO_ERROR;
}
-void HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
memset(halPort, 0, sizeof(audio_port));
halPort->id = port.id;
halPort->role = static_cast<audio_port_role_t>(port.role);
@@ -356,23 +350,20 @@
break;
}
}
+ return NO_ERROR;
}
-void HidlUtils::uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid) {
- uuid->timeLow = halUuid.timeLow;
- uuid->timeMid = halUuid.timeMid;
- uuid->versionAndTimeHigh = halUuid.timeHiAndVersion;
- uuid->variantAndClockSeqHigh = halUuid.clockSeq;
- memcpy(uuid->node.data(), halUuid.node, uuid->node.size());
+#if MAJOR_VERSION >= 5
+status_t HidlUtils::deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+ char* halDeviceAddress) {
+ return deviceAddressToHalImpl(device, halDeviceType, halDeviceAddress);
}
-void HidlUtils::uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid) {
- halUuid->timeLow = uuid.timeLow;
- halUuid->timeMid = uuid.timeMid;
- halUuid->timeHiAndVersion = uuid.versionAndTimeHigh;
- halUuid->clockSeq = uuid.variantAndClockSeqHigh;
- memcpy(halUuid->node, uuid.node.data(), uuid.node.size());
+status_t HidlUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
+ const char* halDeviceAddress, DeviceAddress* device) {
+ return deviceAddressFromHalImpl(halDeviceType, halDeviceAddress, device);
}
+#endif
} // namespace implementation
} // namespace CPP_VERSION
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index ef6dee3..8e9275c 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -23,8 +23,6 @@
#include <system/audio.h>
-using ::android::hardware::hidl_vec;
-
namespace android {
namespace hardware {
namespace audio {
@@ -32,44 +30,215 @@
namespace CPP_VERSION {
namespace implementation {
+using ::android::hardware::hidl_vec;
using namespace ::android::hardware::audio::common::CPP_VERSION;
-class HidlUtils {
- public:
- // A failure here indicates a platform config that is incompatible with
- // the compiled HIDL interface version.
- static status_t audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config);
-
- static void audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
- static void audioGainConfigFromHal(const struct audio_gain_config& halConfig,
- AudioGainConfig* config);
- static void audioGainConfigToHal(const AudioGainConfig& config,
- struct audio_gain_config* halConfig);
- static void audioGainFromHal(const struct audio_gain& halGain, AudioGain* gain);
- static void audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
- static AudioUsage audioUsageFromHal(const audio_usage_t halUsage);
- static audio_usage_t audioUsageToHal(const AudioUsage usage);
- // A failure here indicates a platform offload info that is incompatible with
- // the compiled HIDL interface version.
+struct HidlUtils {
+ static status_t audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+ AudioConfig* config);
+ static status_t audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
+#if MAJOR_VERSION >= 4
+ static status_t audioContentTypeFromHal(const audio_content_type_t halContentType,
+ AudioContentType* contentType);
+ static status_t audioContentTypeToHal(const AudioContentType& contentType,
+ audio_content_type_t* halContentType);
+#endif
+ static status_t audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+ AudioGainConfig* config);
+ static status_t audioGainConfigToHal(const AudioGainConfig& config,
+ struct audio_gain_config* halConfig);
+ static status_t audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+ AudioGain* gain);
+ static status_t audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
+ static status_t audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage);
+ static status_t audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage);
static status_t audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
AudioOffloadInfo* offload);
- static void audioOffloadInfoToHal(const AudioOffloadInfo& offload,
- audio_offload_info_t* halOffload);
- static void audioPortConfigFromHal(const struct audio_port_config& halConfig,
- AudioPortConfig* config);
- static void audioPortConfigToHal(const AudioPortConfig& config,
- struct audio_port_config* halConfig);
- static void audioPortConfigsFromHal(unsigned int numHalConfigs,
- const struct audio_port_config* halConfigs,
- hidl_vec<AudioPortConfig>* configs);
- static std::unique_ptr<audio_port_config[]> audioPortConfigsToHal(
- const hidl_vec<AudioPortConfig>& configs);
- static void audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
- static void audioPortToHal(const AudioPort& port, struct audio_port* halPort);
- static void uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid);
- static void uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid);
+ static status_t audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+ audio_offload_info_t* halOffload);
+ static status_t audioPortConfigFromHal(const struct audio_port_config& halConfig,
+ AudioPortConfig* config);
+ static status_t audioPortConfigToHal(const AudioPortConfig& config,
+ struct audio_port_config* halConfig);
+ static status_t audioPortConfigsFromHal(unsigned int numHalConfigs,
+ const struct audio_port_config* halConfigs,
+ hidl_vec<AudioPortConfig>* configs);
+ static status_t audioPortConfigsToHal(const hidl_vec<AudioPortConfig>& configs,
+ std::unique_ptr<audio_port_config[]>* halConfigs);
+ static status_t audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
+ static status_t audioPortToHal(const AudioPort& port, struct audio_port* halPort);
+ static status_t audioSourceFromHal(audio_source_t halSource, AudioSource* source);
+ static status_t audioSourceToHal(const AudioSource& source, audio_source_t* halSource);
+#if MAJOR_VERSION >= 5
+ static status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+ char* halDeviceAddress);
+ static status_t deviceAddressFromHal(audio_devices_t halDeviceType,
+ const char* halDeviceAddress, DeviceAddress* device);
+#endif
+
+#if MAJOR_VERSION >= 7
+ static constexpr char sAudioTagSeparator = ';';
+
+ static status_t audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+ AudioChannelMask* channelMask);
+ static status_t audioChannelMasksFromHal(const std::vector<std::string>& halChannelMasks,
+ hidl_vec<AudioChannelMask>* channelMasks);
+ static status_t audioChannelMaskToHal(const AudioChannelMask& channelMask,
+ audio_channel_mask_t* halChannelMask);
+ static status_t audioConfigBaseFromHal(const audio_config_base_t& halConfigBase, bool isInput,
+ AudioConfigBase* configBase);
+ static status_t audioConfigBaseToHal(const AudioConfigBase& configBase,
+ audio_config_base_t* halConfigBase);
+ static status_t audioDeviceTypeFromHal(audio_devices_t halDevice, AudioDevice* device);
+ static status_t audioDeviceTypeToHal(const AudioDevice& device, audio_devices_t* halDevice);
+ static status_t audioFormatFromHal(audio_format_t halFormat, AudioFormat* format);
+ static status_t audioFormatsFromHal(const std::vector<std::string>& halFormats,
+ hidl_vec<AudioFormat>* formats);
+ static status_t audioFormatToHal(const AudioFormat& format, audio_format_t* halFormat);
+ static status_t audioGainModeMaskFromHal(audio_gain_mode_t halGainModeMask,
+ hidl_vec<AudioGainMode>* gainModeMask);
+ static status_t audioGainModeMaskToHal(const hidl_vec<AudioGainMode>& gainModeMask,
+ audio_gain_mode_t* halGainModeMask);
+ static status_t audioPortFromHal(const struct audio_port_v7& halPort, AudioPort* port);
+ static status_t audioPortToHal(const AudioPort& port, struct audio_port_v7* halPort);
+ static status_t audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
+ AudioProfile* profile);
+ static status_t audioProfileToHal(const AudioProfile& profile,
+ struct audio_profile* halProfile);
+ static status_t audioStreamTypeFromHal(audio_stream_type_t halStreamType,
+ AudioStreamType* streamType);
+ static status_t audioStreamTypeToHal(const AudioStreamType& streamType,
+ audio_stream_type_t* halStreamType);
+ static status_t audioTagsFromHal(const char* halTags, hidl_vec<AudioTag>* tags);
+ static status_t audioTagsToHal(const hidl_vec<AudioTag>& tags, char* halTags);
+
+ private:
+ static status_t audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+ AudioChannelMask* channelMask);
+ static status_t audioInputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+ AudioChannelMask* channelMask);
+ static status_t audioOutputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+ AudioChannelMask* channelMask);
+ static status_t audioPortExtendedInfoFromHal(
+ audio_port_role_t role, audio_port_type_t type,
+ const struct audio_port_config_device_ext& device,
+ const struct audio_port_config_mix_ext& mix,
+ const struct audio_port_config_session_ext& session, AudioPortExtendedInfo* ext,
+ bool* isInput);
+ static status_t audioPortExtendedInfoToHal(const AudioPortExtendedInfo& ext,
+ audio_port_role_t* role, audio_port_type_t* type,
+ struct audio_port_config_device_ext* device,
+ struct audio_port_config_mix_ext* mix,
+ struct audio_port_config_session_ext* session);
+
+#endif // MAJOR_VERSION >= 7
+
+ // V4 and below have DeviceAddress defined in the 'core' interface.
+ // To avoid duplicating code, the implementations of deviceAddressTo/FromHal
+ // are defined as templates. These templates can be only used directly by V4
+ // and below.
+#if MAJOR_VERSION >= 5
+ private:
+#endif
+ template <typename DA>
+ static status_t deviceAddressToHalImpl(const DA& device, audio_devices_t* halDeviceType,
+ char* halDeviceAddress);
+ template <typename DA>
+ static status_t deviceAddressFromHalImpl(audio_devices_t halDeviceType,
+ const char* halDeviceAddress, DA* device);
};
+#if MAJOR_VERSION <= 6
+#if MAJOR_VERSION >= 4
+inline status_t HidlUtils::audioContentTypeFromHal(const audio_content_type_t halContentType,
+ AudioContentType* contentType) {
+ *contentType = AudioContentType(halContentType);
+ return NO_ERROR;
+}
+
+inline status_t HidlUtils::audioContentTypeToHal(const AudioContentType& contentType,
+ audio_content_type_t* halContentType) {
+ *halContentType = static_cast<audio_content_type_t>(contentType);
+ return NO_ERROR;
+}
+#endif
+
+inline status_t HidlUtils::audioSourceFromHal(audio_source_t halSource, AudioSource* source) {
+ *source = AudioSource(halSource);
+ return NO_ERROR;
+}
+
+inline status_t HidlUtils::audioSourceToHal(const AudioSource& source, audio_source_t* halSource) {
+ *halSource = static_cast<audio_source_t>(source);
+ return NO_ERROR;
+}
+
+template <typename DA>
+status_t HidlUtils::deviceAddressToHalImpl(const DA& device, audio_devices_t* halDeviceType,
+ char* halDeviceAddress) {
+ *halDeviceType = static_cast<audio_devices_t>(device.device);
+ memset(halDeviceAddress, 0, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+ if (audio_is_a2dp_out_device(*halDeviceType) || audio_is_a2dp_in_device(*halDeviceType)) {
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%02X:%02X:%02X:%02X:%02X:%02X",
+ device.address.mac[0], device.address.mac[1], device.address.mac[2],
+ device.address.mac[3], device.address.mac[4], device.address.mac[5]);
+ } else if (*halDeviceType == AUDIO_DEVICE_OUT_IP || *halDeviceType == AUDIO_DEVICE_IN_IP) {
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%d.%d.%d.%d",
+ device.address.ipv4[0], device.address.ipv4[1], device.address.ipv4[2],
+ device.address.ipv4[3]);
+ } else if (audio_is_usb_out_device(*halDeviceType) || audio_is_usb_in_device(*halDeviceType)) {
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "card=%d;device=%d",
+ device.address.alsa.card, device.address.alsa.device);
+ } else if (*halDeviceType == AUDIO_DEVICE_OUT_BUS || *halDeviceType == AUDIO_DEVICE_IN_BUS) {
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s", device.busAddress.c_str());
+ } else if (*halDeviceType == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
+ *halDeviceType == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
+ snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s",
+ device.rSubmixAddress.c_str());
+ }
+ return NO_ERROR;
+}
+
+template <typename DA>
+status_t HidlUtils::deviceAddressFromHalImpl(audio_devices_t halDeviceType,
+ const char* halDeviceAddress, DA* device) {
+ if (device == nullptr) {
+ return BAD_VALUE;
+ }
+ device->device = AudioDevice(halDeviceType);
+ if (halDeviceAddress == nullptr ||
+ strnlen(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) {
+ return NO_ERROR;
+ }
+
+ if (audio_is_a2dp_out_device(halDeviceType) || audio_is_a2dp_in_device(halDeviceType)) {
+ int status =
+ sscanf(halDeviceAddress, "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", &device->address.mac[0],
+ &device->address.mac[1], &device->address.mac[2], &device->address.mac[3],
+ &device->address.mac[4], &device->address.mac[5]);
+ return status == 6 ? OK : BAD_VALUE;
+ } else if (halDeviceType == AUDIO_DEVICE_OUT_IP || halDeviceType == AUDIO_DEVICE_IN_IP) {
+ int status = sscanf(halDeviceAddress, "%hhu.%hhu.%hhu.%hhu", &device->address.ipv4[0],
+ &device->address.ipv4[1], &device->address.ipv4[2],
+ &device->address.ipv4[3]);
+ return status == 4 ? OK : BAD_VALUE;
+ } else if (audio_is_usb_out_device(halDeviceType) || audio_is_usb_in_device(halDeviceType)) {
+ int status = sscanf(halDeviceAddress, "card=%d;device=%d", &device->address.alsa.card,
+ &device->address.alsa.device);
+ return status == 2 ? OK : BAD_VALUE;
+ } else if (halDeviceType == AUDIO_DEVICE_OUT_BUS || halDeviceType == AUDIO_DEVICE_IN_BUS) {
+ device->busAddress = halDeviceAddress;
+ return OK;
+ } else if (halDeviceType == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
+ halDeviceType == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
+ device->rSubmixAddress = halDeviceAddress;
+ return OK;
+ }
+ device->busAddress = halDeviceAddress;
+ return NO_ERROR;
+}
+#endif // MAJOR_VERSION <= 6
+
} // namespace implementation
} // namespace CPP_VERSION
} // namespace common
diff --git a/audio/common/all-versions/default/HidlUtilsCommon.cpp b/audio/common/all-versions/default/HidlUtilsCommon.cpp
new file mode 100644
index 0000000..d2da193
--- /dev/null
+++ b/audio/common/all-versions/default/HidlUtilsCommon.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#include "HidlUtils.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+status_t HidlUtils::audioPortConfigsFromHal(unsigned int numHalConfigs,
+ const struct audio_port_config* halConfigs,
+ hidl_vec<AudioPortConfig>* configs) {
+ status_t result = NO_ERROR;
+ configs->resize(numHalConfigs);
+ for (unsigned int i = 0; i < numHalConfigs; ++i) {
+ if (status_t status = audioPortConfigFromHal(halConfigs[i], &(*configs)[i]);
+ status != NO_ERROR) {
+ result = status;
+ }
+ }
+ return result;
+}
+
+status_t HidlUtils::audioPortConfigsToHal(const hidl_vec<AudioPortConfig>& configs,
+ std::unique_ptr<audio_port_config[]>* halConfigs) {
+ status_t result = NO_ERROR;
+ halConfigs->reset(new audio_port_config[configs.size()]);
+ for (size_t i = 0; i < configs.size(); ++i) {
+ if (status_t status = audioPortConfigToHal(configs[i], &(*halConfigs)[i]);
+ status != NO_ERROR) {
+ result = status;
+ }
+ }
+ return result;
+}
+
+} // namespace implementation
+} // namespace CPP_VERSION
+} // namespace common
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/common/all-versions/default/TEST_MAPPING b/audio/common/all-versions/default/TEST_MAPPING
new file mode 100644
index 0000000..4316ccf
--- /dev/null
+++ b/audio/common/all-versions/default/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "android.hardware.audio.common@7.0-util_tests"
+ }
+ ]
+}
diff --git a/audio/common/all-versions/default/UuidUtils.cpp b/audio/common/all-versions/default/UuidUtils.cpp
new file mode 100644
index 0000000..85edc7b
--- /dev/null
+++ b/audio/common/all-versions/default/UuidUtils.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include "UuidUtils.h"
+
+#include <common/all-versions/VersionUtils.h>
+#include <string.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+void UuidUtils::uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid) {
+ uuid->timeLow = halUuid.timeLow;
+ uuid->timeMid = halUuid.timeMid;
+ uuid->versionAndTimeHigh = halUuid.timeHiAndVersion;
+ uuid->variantAndClockSeqHigh = halUuid.clockSeq;
+ memcpy(uuid->node.data(), halUuid.node, uuid->node.size());
+}
+
+void UuidUtils::uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid) {
+ halUuid->timeLow = uuid.timeLow;
+ halUuid->timeMid = uuid.timeMid;
+ halUuid->timeHiAndVersion = uuid.versionAndTimeHigh;
+ halUuid->clockSeq = uuid.variantAndClockSeqHigh;
+ memcpy(halUuid->node, uuid.node.data(), uuid.node.size());
+}
+
+} // namespace implementation
+} // namespace CPP_VERSION
+} // namespace common
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/common/all-versions/default/UuidUtils.h b/audio/common/all-versions/default/UuidUtils.h
new file mode 100644
index 0000000..38db48a
--- /dev/null
+++ b/audio/common/all-versions/default/UuidUtils.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef android_hardware_audio_Uuid_Utils_H_
+#define android_hardware_audio_Uuid_Utils_H_
+
+// clang-format off
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+#include <system/audio.h>
+
+using ::android::hardware::hidl_vec;
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+class UuidUtils {
+ public:
+ static void uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid);
+ static void uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid);
+};
+
+} // namespace implementation
+} // namespace CPP_VERSION
+} // namespace common
+} // namespace audio
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_audio_Uuid_Utils_H_
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
new file mode 100644
index 0000000..fef88b4
--- /dev/null
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -0,0 +1,762 @@
+/*
+ * 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.
+ */
+
+#include <array>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "HidlUtils_Test"
+#include <log/log.h>
+
+#include <HidlUtils.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <system/audio.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+using ::android::hardware::hidl_vec;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+static constexpr audio_channel_mask_t kInvalidHalChannelMask =
+ static_cast<audio_channel_mask_t>(0xFFFFFFFFU);
+static constexpr audio_content_type_t kInvalidHalContentType =
+ static_cast<audio_content_type_t>(0xFFFFFFFFU);
+static constexpr audio_devices_t kInvalidHalDevice = static_cast<audio_devices_t>(0xFFFFFFFFU);
+static constexpr audio_format_t kInvalidHalFormat = static_cast<audio_format_t>(0xFFFFFFFFU);
+static constexpr audio_gain_mode_t kInvalidHalGainMode =
+ static_cast<audio_gain_mode_t>(0xFFFFFFFFU);
+static constexpr audio_source_t kInvalidHalSource = static_cast<audio_source_t>(0xFFFFFFFFU);
+static constexpr audio_stream_type_t kInvalidHalStreamType =
+ static_cast<audio_stream_type_t>(0xFFFFFFFFU);
+static constexpr audio_usage_t kInvalidHalUsage = static_cast<audio_usage_t>(0xFFFFFFFFU);
+
+TEST(HidlUtils, ConvertInvalidChannelMask) {
+ AudioChannelMask invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(AUDIO_CHANNEL_INVALID,
+ false /*isInput*/, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(AUDIO_CHANNEL_INVALID, true /*isInput*/,
+ &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(kInvalidHalChannelMask,
+ false /*isInput*/, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(kInvalidHalChannelMask,
+ true /*isInput*/, &invalid));
+ audio_channel_mask_t halInvalid;
+ // INVALID channel mask is not in XSD thus it's not allowed for transfer over HIDL.
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("AUDIO_CHANNEL_INVALID", &halInvalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("random string", &halInvalid));
+}
+
+// Might move these to the audio_policy_configuration_V7_0-enums library
+// if there would be usages in the default wrapper code. In that case,
+// it would be better to reimplement these methods using a proper switch statement
+// over all known enum values.
+static bool isInputChannelMask(xsd::AudioChannelMask channelMask) {
+ return toString(channelMask).find("_CHANNEL_IN_") != std::string::npos;
+}
+
+static bool isOutputChannelMask(xsd::AudioChannelMask channelMask) {
+ return toString(channelMask).find("_CHANNEL_OUT_") != std::string::npos;
+}
+
+static bool isIndexChannelMask(xsd::AudioChannelMask channelMask) {
+ return toString(channelMask).find("_CHANNEL_INDEX_") != std::string::npos;
+}
+
+TEST(HidlUtils, ConvertChannelMask) {
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+ const AudioChannelMask channelMask = toString(enumVal);
+ audio_channel_mask_t halChannelMask, halChannelMaskBack;
+ AudioChannelMask channelMaskBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskToHal(channelMask, &halChannelMask))
+ << "Conversion of \"" << channelMask << "\" failed";
+ EXPECT_EQ(enumVal != xsd::AudioChannelMask::AUDIO_CHANNEL_NONE,
+ audio_channel_mask_is_valid(halChannelMask))
+ << "Validity of \"" << channelMask << "\" is not as expected";
+ if (bool isInput = isInputChannelMask(enumVal); isInput || isOutputChannelMask(enumVal)) {
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::audioChannelMaskFromHal(halChannelMask, isInput, &channelMaskBack))
+ << "Conversion of " << (isInput ? "input" : "output") << " channel mask "
+ << halChannelMask << " failed";
+ // Due to aliased values, the result of 'fromHal' might not be the same
+ // as 'channelMask', thus we need to compare the results of 'toHal' conversion instead.
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::audioChannelMaskToHal(channelMaskBack, &halChannelMaskBack))
+ << "Conversion of \"" << channelMaskBack << "\" failed";
+ EXPECT_EQ(halChannelMask, halChannelMaskBack);
+ } else if (isIndexChannelMask(enumVal) ||
+ enumVal == xsd::AudioChannelMask::AUDIO_CHANNEL_NONE) {
+ // Conversions for indexed masks and "none" must not depend on the provided direction.
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskFromHal(halChannelMask, true /*isInput*/,
+ &channelMaskBack))
+ << "Conversion of indexed / none channel mask " << halChannelMask
+ << " failed (as input channel mask)";
+ EXPECT_EQ(channelMask, channelMaskBack);
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskFromHal(
+ halChannelMask, false /*isInput*/, &channelMaskBack))
+ << "Conversion of indexed / none channel mask " << halChannelMask
+ << " failed (as output channel mask)";
+ EXPECT_EQ(channelMask, channelMaskBack);
+ } else {
+ FAIL() << "Unrecognized channel mask \"" << channelMask << "\"";
+ }
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidChannelMasksFromHal) {
+ std::vector<std::string> validAndInvalidChannelMasks = {
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO), "random string", ""};
+ hidl_vec<AudioChannelMask> validChannelMask;
+ EXPECT_EQ(BAD_VALUE,
+ HidlUtils::audioChannelMasksFromHal(validAndInvalidChannelMasks, &validChannelMask));
+ EXPECT_EQ(1, validChannelMask.size());
+ EXPECT_EQ(validAndInvalidChannelMasks[0], validChannelMask[0]);
+
+ std::vector<std::string> invalidChannelMasks = {"random string", ""};
+ hidl_vec<AudioChannelMask> empty;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMasksFromHal(invalidChannelMasks, &empty));
+ EXPECT_EQ(0, empty.size());
+}
+
+TEST(HidlUtils, ConvertChannelMasksFromHal) {
+ std::vector<std::string> allHalChannelMasks;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+ allHalChannelMasks.push_back(toString(enumVal));
+ }
+ hidl_vec<AudioChannelMask> allChannelMasks;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMasksFromHal(allHalChannelMasks, &allChannelMasks));
+ EXPECT_EQ(allHalChannelMasks.size(), allChannelMasks.size());
+ for (size_t i = 0; i < allHalChannelMasks.size(); ++i) {
+ EXPECT_EQ(allHalChannelMasks[i], allChannelMasks[i]);
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidConfigBase) {
+ AudioConfigBase invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal({.sample_rate = 0,
+ .channel_mask = kInvalidHalChannelMask,
+ .format = kInvalidHalFormat},
+ false /*isInput*/, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal({.sample_rate = 0,
+ .channel_mask = kInvalidHalChannelMask,
+ .format = kInvalidHalFormat},
+ true /*isInput*/, &invalid));
+ audio_config_base_t halInvalid;
+ invalid.sampleRateHz = 0;
+ invalid.channelMask = "random string";
+ invalid.format = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfigBase) {
+ AudioConfigBase configBase;
+ configBase.sampleRateHz = 44100;
+ configBase.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ configBase.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+ audio_config_base_t halConfigBase;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseToHal(configBase, &halConfigBase));
+ AudioConfigBase configBaseBack;
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::audioConfigBaseFromHal(halConfigBase, false /*isInput*/, &configBaseBack));
+ EXPECT_EQ(configBase, configBaseBack);
+}
+
+TEST(HidlUtils, ConvertInvalidContentType) {
+ AudioContentType invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioContentTypeFromHal(kInvalidHalContentType, &invalid));
+ audio_content_type_t halInvalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioContentTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertContentType) {
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioContentType>{}) {
+ const AudioContentType contentType = toString(enumVal);
+ audio_content_type_t halContentType;
+ AudioContentType contentTypeBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioContentTypeToHal(contentType, &halContentType))
+ << "Conversion of \"" << contentType << "\" failed";
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioContentTypeFromHal(halContentType, &contentTypeBack))
+ << "Conversion of content type " << halContentType << " failed";
+ EXPECT_EQ(contentType, contentTypeBack);
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidDeviceType) {
+ AudioDevice invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeFromHal(kInvalidHalDevice, &invalid));
+ audio_devices_t halInvalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertDeviceType) {
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioDevice>{}) {
+ const AudioDevice deviceType = toString(enumVal);
+ audio_devices_t halDeviceType, halDeviceTypeBack;
+ AudioDevice deviceTypeBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeToHal(deviceType, &halDeviceType))
+ << "Conversion of \"" << deviceType << "\" failed";
+ if (enumVal != xsd::AudioDevice::AUDIO_DEVICE_NONE) {
+ EXPECT_TRUE(audio_is_input_device(halDeviceType) ||
+ audio_is_output_device(halDeviceType))
+ << "Device \"" << deviceType << "\" is neither input, nor output device";
+ } else {
+ EXPECT_FALSE(audio_is_input_device(halDeviceType));
+ EXPECT_FALSE(audio_is_output_device(halDeviceType));
+ }
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeFromHal(halDeviceType, &deviceTypeBack))
+ << "Conversion of device type " << halDeviceType << " failed";
+ // Due to aliased values, the result of 'fromHal' might not be the same
+ // as 'deviceType', thus we need to compare the results of 'toHal' conversion instead.
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeToHal(deviceTypeBack, &halDeviceTypeBack))
+ << "Conversion of \"" << deviceTypeBack << "\" failed";
+ EXPECT_EQ(halDeviceType, halDeviceTypeBack);
+ }
+}
+
+// The enums module is too small to have unit tests on its own.
+TEST(HidlUtils, VendorExtension) {
+ EXPECT_TRUE(xsd::isVendorExtension("VX_GOOGLE_VR_42"));
+ EXPECT_FALSE(xsd::isVendorExtension("random string"));
+ EXPECT_FALSE(xsd::isVendorExtension("VX_"));
+ EXPECT_FALSE(xsd::isVendorExtension("VX_GOOGLE_$$"));
+}
+
+TEST(HidlUtils, ConvertInvalidDeviceAddress) {
+ DeviceAddress invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+ nullptr, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+ "", &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_IP, nullptr, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_IP, "", &invalid));
+ EXPECT_EQ(BAD_VALUE,
+ HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_USB_HEADSET, nullptr, &invalid));
+ EXPECT_EQ(BAD_VALUE,
+ HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_USB_HEADSET, "", &invalid));
+
+ audio_devices_t halInvalid;
+ char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+ invalid = {};
+ invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+ EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+ invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_IP);
+ EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+ invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET);
+ EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+}
+
+static void ConvertDeviceAddress(const DeviceAddress& device) {
+ audio_devices_t halDeviceType;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+ EXPECT_EQ(NO_ERROR, HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress));
+ DeviceAddress deviceBack;
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, &deviceBack));
+ EXPECT_EQ(device, deviceBack);
+}
+
+TEST(HidlUtils, ConvertUniqueDeviceAddress) {
+ DeviceAddress speaker;
+ speaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+ ConvertDeviceAddress(speaker);
+}
+
+TEST(HidlUtils, ConvertA2dpDeviceAddress) {
+ DeviceAddress a2dpSpeaker;
+ a2dpSpeaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+ a2dpSpeaker.address.mac(std::array<uint8_t, 6>{1, 2, 3, 4, 5, 6});
+ ConvertDeviceAddress(a2dpSpeaker);
+}
+
+TEST(HidlUtils, ConvertIpv4DeviceAddress) {
+ DeviceAddress ipv4;
+ ipv4.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_IP);
+ ipv4.address.ipv4(std::array<uint8_t, 4>{1, 2, 3, 4});
+ ConvertDeviceAddress(ipv4);
+}
+
+TEST(HidlUtils, ConvertUsbDeviceAddress) {
+ DeviceAddress usbHeadset;
+ usbHeadset.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET);
+ usbHeadset.address.alsa({1, 2});
+ ConvertDeviceAddress(usbHeadset);
+}
+
+TEST(HidlUtils, ConvertBusDeviceAddress) {
+ DeviceAddress bus;
+ bus.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS);
+ bus.address.id("bus_device");
+ ConvertDeviceAddress(bus);
+}
+
+TEST(HidlUtils, ConvertRSubmixDeviceAddress) {
+ DeviceAddress rSubmix;
+ rSubmix.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_REMOTE_SUBMIX);
+ rSubmix.address.id(AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
+ ConvertDeviceAddress(rSubmix);
+}
+
+TEST(HidlUtils, ConvertVendorDeviceAddress) {
+ // The address part is not mandatory, both cases must work.
+ {
+ DeviceAddress vendor;
+ vendor.deviceType = "VX_GOOGLE_VR";
+ audio_devices_t halDeviceType;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+ // Ignore the result. Vendors will also add the extended device into
+ // the list of devices in audio-hal-enums.h. Without that, the conversion
+ // officially fails, but it still maps the device type to NONE.
+ (void)HidlUtils::deviceAddressToHal(vendor, &halDeviceType, halDeviceAddress);
+ EXPECT_EQ(AUDIO_DEVICE_NONE, halDeviceType);
+ EXPECT_EQ(0, strnlen(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+ }
+ {
+ DeviceAddress vendor;
+ vendor.deviceType = "VX_GOOGLE_VR";
+ vendor.address.id("vr1");
+ audio_devices_t halDeviceType;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+ // Ignore the result. Vendors will also add the extended device into
+ // the list of devices in audio-hal-enums.h. Without that, the conversion
+ // officially fails, but it still maps the device type to NONE and converts
+ // the address.
+ (void)HidlUtils::deviceAddressToHal(vendor, &halDeviceType, halDeviceAddress);
+ EXPECT_EQ(AUDIO_DEVICE_NONE, halDeviceType);
+ EXPECT_EQ(0, strncmp("vr1", halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidFormat) {
+ AudioFormat invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatFromHal(kInvalidHalFormat, &invalid));
+ audio_format_t halInvalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertFormat) {
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
+ const AudioFormat format = toString(enumVal);
+ audio_format_t halFormat;
+ AudioFormat formatBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatToHal(format, &halFormat))
+ << "Conversion of \"" << format << "\" failed";
+ EXPECT_TRUE(audio_is_valid_format(halFormat))
+ << "Converted format \"" << format << "\" is invalid";
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatFromHal(halFormat, &formatBack))
+ << "Conversion of format " << halFormat << " failed";
+ EXPECT_EQ(format, formatBack);
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidFormatsFromHal) {
+ std::vector<std::string> validAndInvalidFormats = {
+ toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT), "random string", ""};
+ hidl_vec<AudioFormat> validFormat;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatsFromHal(validAndInvalidFormats, &validFormat));
+ EXPECT_EQ(1, validFormat.size());
+ EXPECT_EQ(validAndInvalidFormats[0], validFormat[0]);
+
+ std::vector<std::string> invalidFormats = {"random string", ""};
+ hidl_vec<AudioFormat> empty;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatsFromHal(invalidFormats, &empty));
+ EXPECT_EQ(0, empty.size());
+}
+
+TEST(HidlUtils, ConvertFormatsFromHal) {
+ std::vector<std::string> allHalFormats;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
+ allHalFormats.push_back(toString(enumVal));
+ }
+ hidl_vec<AudioFormat> allFormats;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatsFromHal(allHalFormats, &allFormats));
+ EXPECT_EQ(allHalFormats.size(), allFormats.size());
+ for (size_t i = 0; i < allHalFormats.size(); ++i) {
+ EXPECT_EQ(allHalFormats[i], allFormats[i]);
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidGainModeMask) {
+ hidl_vec<AudioGainMode> invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainModeMaskFromHal(kInvalidHalGainMode, &invalid));
+ audio_gain_mode_t halInvalid;
+ invalid.resize(1);
+ invalid[0] = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainModeMaskToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGainModeMask) {
+ hidl_vec<AudioGainMode> emptyGainModes;
+ audio_gain_mode_t halEmptyGainModes;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskToHal(emptyGainModes, &halEmptyGainModes));
+ hidl_vec<AudioGainMode> emptyGainModesBack;
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::audioGainModeMaskFromHal(halEmptyGainModes, &emptyGainModesBack));
+ EXPECT_EQ(emptyGainModes, emptyGainModesBack);
+
+ std::vector<std::string> allEnumValues;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioGainMode>{}) {
+ allEnumValues.push_back(toString(enumVal));
+ }
+ hidl_vec<AudioGainMode> allGainModes;
+ allGainModes.resize(allEnumValues.size());
+ for (size_t i = 0; i < allEnumValues.size(); ++i) {
+ allGainModes[i] = allEnumValues[i];
+ }
+ audio_gain_mode_t halAllGainModes;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskToHal(allGainModes, &halAllGainModes));
+ hidl_vec<AudioGainMode> allGainModesBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskFromHal(halAllGainModes, &allGainModesBack));
+ EXPECT_EQ(allGainModes, allGainModesBack);
+}
+
+TEST(HidlUtils, ConvertInvalidSource) {
+ AudioSource invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceFromHal(kInvalidHalSource, &invalid));
+ audio_source_t halInvalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertSource) {
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioSource>{}) {
+ const AudioSource source = toString(enumVal);
+ audio_source_t halSource;
+ AudioSource sourceBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioSourceToHal(source, &halSource))
+ << "Conversion of \"" << source << "\" failed";
+ EXPECT_EQ(enumVal != xsd::AudioSource::AUDIO_SOURCE_DEFAULT,
+ audio_is_valid_audio_source(halSource))
+ << "Validity of \"" << source << "\" is not as expected";
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioSourceFromHal(halSource, &sourceBack))
+ << "Conversion of source " << halSource << " failed";
+ EXPECT_EQ(source, sourceBack);
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidStreamType) {
+ AudioStreamType invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioStreamTypeFromHal(kInvalidHalStreamType, &invalid));
+ audio_stream_type_t halInvalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioStreamTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertStreamType) {
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioStreamType>{}) {
+ const AudioStreamType streamType = toString(enumVal);
+ audio_stream_type_t halStreamType;
+ AudioStreamType streamTypeBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeToHal(streamType, &halStreamType))
+ << "Conversion of \"" << streamType << "\" failed";
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeFromHal(halStreamType, &streamTypeBack))
+ << "Conversion of stream type " << halStreamType << " failed";
+ EXPECT_EQ(streamType, streamTypeBack);
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidGain) {
+ AudioGain invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainFromHal({.mode = kInvalidHalGainMode},
+ false /*isInput*/, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainFromHal({.mode = kInvalidHalGainMode},
+ true /*isInput*/, &invalid));
+ struct audio_gain halInvalid;
+ invalid.mode.resize(1);
+ invalid.mode[0] = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGain) {
+ AudioGain gain = {};
+ gain.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ struct audio_gain halGain;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioGainToHal(gain, &halGain));
+ AudioGain gainBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioGainFromHal(halGain, false /*isInput*/, &gainBack));
+ EXPECT_EQ(gain, gainBack);
+ struct audio_gain halGainBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioGainToHal(gainBack, &halGainBack));
+ EXPECT_TRUE(audio_gains_are_equal(&halGain, &halGainBack));
+}
+
+TEST(HidlUtils, ConvertInvalidGainConfig) {
+ AudioGainConfig invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigFromHal({.mode = kInvalidHalGainMode},
+ false /*isInput*/, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigFromHal({.mode = kInvalidHalGainMode},
+ true /*isInput*/, &invalid));
+ struct audio_gain_config halInvalid;
+ invalid.mode.resize(1);
+ invalid.mode[0] = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGainConfig) {
+ AudioGainConfig gainConfig = {};
+ gainConfig.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ struct audio_gain_config halGainConfig;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioGainConfigToHal(gainConfig, &halGainConfig));
+ AudioGainConfig gainConfigBack;
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::audioGainConfigFromHal(halGainConfig, false /*isInput*/, &gainConfigBack));
+ EXPECT_EQ(gainConfig, gainConfigBack);
+ struct audio_gain_config halGainConfigBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioGainConfigToHal(gainConfigBack, &halGainConfigBack));
+ EXPECT_TRUE(audio_gain_config_are_equal(&halGainConfig, &halGainConfigBack));
+}
+
+TEST(HidlUtils, ConvertInvalidUsage) {
+ AudioUsage invalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageFromHal(kInvalidHalUsage, &invalid));
+ audio_usage_t halInvalid;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertUsage) {
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioUsage>{}) {
+ const AudioUsage usage = toString(enumVal);
+ audio_usage_t halUsage;
+ AudioUsage usageBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioUsageToHal(usage, &halUsage))
+ << "Conversion of \"" << usage << "\" failed";
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioUsageFromHal(halUsage, &usageBack))
+ << "Conversion of usage " << halUsage << " failed";
+ EXPECT_EQ(usage, usageBack);
+ }
+}
+
+TEST(HidlUtils, ConvertInvalidOffloadInfo) {
+ AudioOffloadInfo invalid;
+ audio_offload_info_t halInvalid = AUDIO_INFO_INITIALIZER;
+ halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+ halInvalid.format = kInvalidHalFormat;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoFromHal(halInvalid, &invalid));
+ invalid.base.channelMask = "random string";
+ invalid.base.format = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertOffloadInfo) {
+ AudioOffloadInfo offloadInfo = {};
+ offloadInfo.base.sampleRateHz = 44100;
+ offloadInfo.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ offloadInfo.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+ offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
+ offloadInfo.bitRatePerSecond = 320;
+ offloadInfo.durationMicroseconds = -1;
+ offloadInfo.bitWidth = 16;
+ offloadInfo.bufferSize = 1024;
+ offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
+ offloadInfo.encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM;
+ offloadInfo.contentId = 42;
+ offloadInfo.syncId = 13;
+ audio_offload_info_t halOffloadInfo;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioOffloadInfoToHal(offloadInfo, &halOffloadInfo));
+ AudioOffloadInfo offloadInfoBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioOffloadInfoFromHal(halOffloadInfo, &offloadInfoBack));
+ EXPECT_EQ(offloadInfo, offloadInfoBack);
+}
+
+TEST(HidlUtils, ConvertInvalidConfig) {
+ AudioConfig invalid;
+ audio_config_t halInvalid = AUDIO_CONFIG_INITIALIZER;
+ halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+ halInvalid.format = kInvalidHalFormat;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigFromHal(halInvalid, false /*isInput*/, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigFromHal(halInvalid, true /*isInput*/, &invalid));
+ invalid.base.channelMask = "random string";
+ invalid.base.format = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfig) {
+ AudioConfig config = {};
+ config.base.sampleRateHz = 44100;
+ config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+ audio_config_t halConfig;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(config, &halConfig));
+ AudioConfig configBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, &configBack));
+ EXPECT_EQ(config, configBack);
+}
+
+TEST(HidlUtils, ConvertConfigWithOffloadInfo) {
+ AudioConfig config = {};
+ config.base.sampleRateHz = 44100;
+ config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+ config.offloadInfo.info(
+ AudioOffloadInfo{.base = config.base,
+ .streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
+ .bitRatePerSecond = 320,
+ .durationMicroseconds = -1,
+ .bitWidth = 16,
+ .bufferSize = 1024,
+ .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+ .encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM,
+ .contentId = 42,
+ .syncId = 13});
+ audio_config_t halConfig;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(config, &halConfig));
+ AudioConfig configBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, &configBack));
+ EXPECT_EQ(config, configBack);
+}
+
+TEST(HidlUtils, ConvertInvalidAudioProfile) {
+ AudioProfile invalid;
+ struct audio_profile halInvalid = {};
+ halInvalid.format = kInvalidHalFormat;
+ halInvalid.num_sample_rates = 0;
+ halInvalid.num_channel_masks = 1;
+ halInvalid.channel_masks[0] = kInvalidHalChannelMask;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileFromHal(halInvalid, false /*isInput*/, &invalid));
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileFromHal(halInvalid, true /*isInput*/, &invalid));
+ invalid.format = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioProfile) {
+ 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);
+ struct audio_profile halProfile;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioProfileToHal(profile, &halProfile));
+ AudioProfile profileBack;
+ EXPECT_EQ(NO_ERROR,
+ HidlUtils::audioProfileFromHal(halProfile, false /*isInput*/, &profileBack));
+ EXPECT_EQ(profile, profileBack);
+}
+
+TEST(HidlUtils, ConvertInvalidAudioPortConfig) {
+ AudioPortConfig invalid;
+ struct audio_port_config halInvalid = {};
+ halInvalid.type = AUDIO_PORT_TYPE_MIX;
+ halInvalid.role = AUDIO_PORT_ROLE_NONE; // note: this is valid.
+ halInvalid.config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
+ halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigFromHal(halInvalid, &invalid));
+ invalid.base.channelMask = "random string";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioPortConfig) {
+ AudioPortConfig config = {};
+ config.id = 42;
+ config.base.sampleRateHz = 44100;
+ config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+ config.gain.config({});
+ config.gain.config().channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ config.ext.device({});
+ config.ext.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+ struct audio_port_config halConfig;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigToHal(config, &halConfig));
+ AudioPortConfig configBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigFromHal(halConfig, &configBack));
+ EXPECT_EQ(config, configBack);
+ struct audio_port_config halConfigBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigToHal(configBack, &halConfigBack));
+ EXPECT_TRUE(audio_port_configs_are_equal(&halConfig, &halConfigBack));
+}
+
+TEST(HidlUtils, ConvertInvalidAudioPort) {
+ AudioPort invalid;
+ struct audio_port_v7 halInvalid = {};
+ halInvalid.type = AUDIO_PORT_TYPE_MIX;
+ halInvalid.role = AUDIO_PORT_ROLE_NONE; // note: this is valid.
+ 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";
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioPort) {
+ 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.gains.resize(1);
+ port.gains[0].channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+ port.ext.device({});
+ port.ext.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+ // active config left unspecified.
+ struct audio_port_v7 halPort;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(port, &halPort));
+ AudioPort portBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioPortFromHal(halPort, &portBack));
+ EXPECT_EQ(port, portBack);
+ struct audio_port_v7 halPortBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(portBack, &halPortBack));
+ EXPECT_TRUE(audio_ports_v7_are_equal(&halPort, &halPortBack));
+}
+
+TEST(HidlUtils, ConvertInvalidAudioTags) {
+ char halTag[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
+
+ hidl_vec<AudioTag> emptyTag = {{""}};
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(emptyTag, halTag));
+
+ hidl_vec<AudioTag> longTag = {{std::string(AUDIO_ATTRIBUTES_TAGS_MAX_SIZE + 1, 'A')}};
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(longTag, halTag));
+
+ hidl_vec<AudioTag> tagSeparator = {
+ {std::string(AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1, HidlUtils::sAudioTagSeparator)}};
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(tagSeparator, halTag));
+
+ hidl_vec<AudioTag> notExtensions = {{"random string", "VX_", "VX_GOOGLE_$$"}};
+ EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(notExtensions, halTag));
+}
+
+TEST(HidlUtils, ConvertAudioTags) {
+ hidl_vec<AudioTag> emptyTags;
+ char halEmptyTags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(emptyTags, halEmptyTags));
+ hidl_vec<AudioTag> emptyTagsBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsFromHal(halEmptyTags, &emptyTagsBack));
+ EXPECT_EQ(emptyTags, emptyTagsBack);
+
+ hidl_vec<AudioTag> oneTag = {{"VX_GOOGLE_VR"}};
+ char halOneTag[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(oneTag, halOneTag));
+ hidl_vec<AudioTag> oneTagBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsFromHal(halOneTag, &oneTagBack));
+ EXPECT_EQ(oneTag, oneTagBack);
+
+ hidl_vec<AudioTag> twoTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
+ char halTwoTags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(twoTags, halTwoTags));
+ hidl_vec<AudioTag> twoTagsBack;
+ EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsFromHal(halTwoTags, &twoTagsBack));
+ EXPECT_EQ(twoTags, twoTagsBack);
+}
diff --git a/audio/common/all-versions/util/include/common/all-versions/HidlSupport.h b/audio/common/all-versions/util/include/common/all-versions/HidlSupport.h
index b514a43..d7802cf 100644
--- a/audio/common/all-versions/util/include/common/all-versions/HidlSupport.h
+++ b/audio/common/all-versions/util/include/common/all-versions/HidlSupport.h
@@ -20,6 +20,9 @@
#include <hidl/HidlSupport.h>
#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
namespace android::hardware::audio::common::utils {
@@ -29,6 +32,16 @@
return std::find(values.begin(), values.end(), e) != values.end();
}
+static inline std::vector<std::string> splitString(const std::string& s, char separator) {
+ std::istringstream iss(s);
+ std::string t;
+ std::vector<std::string> result;
+ while (std::getline(iss, t, separator)) {
+ result.push_back(std::move(t));
+ }
+ return result;
+}
+
} // namespace android::hardware::audio::common::utils
#endif // android_hardware_audio_common_HidlSupport_H_
diff --git a/audio/core/all-versions/default/Android.bp b/audio/core/all-versions/default/Android.bp
index 6be0628..e0f0860 100644
--- a/audio/core/all-versions/default/Android.bp
+++ b/audio/core/all-versions/default/Android.bp
@@ -125,13 +125,15 @@
}
cc_library_shared {
- enabled: false,
name: "android.hardware.audio@7.0-impl",
defaults: ["android.hardware.audio-impl_default"],
shared_libs: [
"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",
+ "libbase",
+ "libxml2",
],
cflags: [
"-DMAJOR_VERSION=7",
diff --git a/audio/core/all-versions/default/Conversions.cpp b/audio/core/all-versions/default/Conversions.cpp
index 0db210a..f1752cc 100644
--- a/audio/core/all-versions/default/Conversions.cpp
+++ b/audio/core/all-versions/default/Conversions.cpp
@@ -18,8 +18,11 @@
#include <stdio.h>
+#if MAJOR_VERSION >= 7
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#endif
+#include <HidlUtils.h>
#include <log/log.h>
-#include <media/AudioContainers.h>
namespace android {
namespace hardware {
@@ -27,72 +30,32 @@
namespace CPP_VERSION {
namespace implementation {
-std::string deviceAddressToHal(const DeviceAddress& address) {
- // HAL assumes that the address is NUL-terminated.
- char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
- memset(halAddress, 0, sizeof(halAddress));
- audio_devices_t halDevice = static_cast<audio_devices_t>(address.device);
- if (getAudioDeviceOutAllA2dpSet().count(halDevice) > 0 ||
- halDevice == AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
- snprintf(halAddress, sizeof(halAddress), "%02X:%02X:%02X:%02X:%02X:%02X",
- address.address.mac[0], address.address.mac[1], address.address.mac[2],
- address.address.mac[3], address.address.mac[4], address.address.mac[5]);
- } else if (halDevice == AUDIO_DEVICE_OUT_IP || halDevice == AUDIO_DEVICE_IN_IP) {
- snprintf(halAddress, sizeof(halAddress), "%d.%d.%d.%d", address.address.ipv4[0],
- address.address.ipv4[1], address.address.ipv4[2], address.address.ipv4[3]);
- } else if (getAudioDeviceOutAllUsbSet().count(halDevice) > 0 ||
- getAudioDeviceInAllUsbSet().count(halDevice) > 0) {
- snprintf(halAddress, sizeof(halAddress), "card=%d;device=%d", address.address.alsa.card,
- address.address.alsa.device);
- } else if (halDevice == AUDIO_DEVICE_OUT_BUS || halDevice == AUDIO_DEVICE_IN_BUS) {
- snprintf(halAddress, sizeof(halAddress), "%s", address.busAddress.c_str());
- } else if (halDevice == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
- halDevice == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
- snprintf(halAddress, sizeof(halAddress), "%s", address.rSubmixAddress.c_str());
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+
+#define CONVERT_CHECKED(expr, result) \
+ if (status_t status = (expr); status != NO_ERROR) { \
+ result = status; \
}
- return halAddress;
+
+status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+ char* halDeviceAddress) {
+#if MAJOR_VERSION >= 5
+ return HidlUtils::deviceAddressToHal(device, halDeviceType, halDeviceAddress);
+#else
+ return HidlUtils::deviceAddressToHalImpl(device, halDeviceType, halDeviceAddress);
+#endif
+}
+
+status_t deviceAddressFromHal(audio_devices_t halDeviceType, const char* halDeviceAddress,
+ DeviceAddress* device) {
+#if MAJOR_VERSION >= 5
+ return HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, device);
+#else
+ return HidlUtils::deviceAddressFromHalImpl(halDeviceType, halDeviceAddress, device);
+#endif
}
#if MAJOR_VERSION >= 4
-status_t deviceAddressFromHal(audio_devices_t device, const char* halAddress,
- DeviceAddress* address) {
- if (address == nullptr) {
- return BAD_VALUE;
- }
- address->device = AudioDevice(device);
- if (halAddress == nullptr || strnlen(halAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) {
- return OK;
- }
-
- if (getAudioDeviceOutAllA2dpSet().count(device) > 0 ||
- device == AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
- int status =
- sscanf(halAddress, "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", &address->address.mac[0],
- &address->address.mac[1], &address->address.mac[2], &address->address.mac[3],
- &address->address.mac[4], &address->address.mac[5]);
- return status == 6 ? OK : BAD_VALUE;
- } else if (device == AUDIO_DEVICE_OUT_IP || device == AUDIO_DEVICE_IN_IP) {
- int status =
- sscanf(halAddress, "%hhu.%hhu.%hhu.%hhu", &address->address.ipv4[0],
- &address->address.ipv4[1], &address->address.ipv4[2], &address->address.ipv4[3]);
- return status == 4 ? OK : BAD_VALUE;
- } else if (getAudioDeviceOutAllUsbSet().count(device) > 0 ||
- getAudioDeviceInAllUsbSet().count(device) > 0) {
- int status = sscanf(halAddress, "card=%d;device=%d", &address->address.alsa.card,
- &address->address.alsa.device);
- return status == 2 ? OK : BAD_VALUE;
- } else if (device == AUDIO_DEVICE_OUT_BUS || device == AUDIO_DEVICE_IN_BUS) {
- address->busAddress = halAddress;
- return OK;
- } else if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
- device == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
- address->rSubmixAddress = halAddress;
- return OK;
- }
- address->busAddress = halAddress;
- return OK;
-}
-
bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst,
const struct audio_microphone_characteristic_t& src) {
bool status = false;
@@ -130,6 +93,134 @@
}
return status;
}
+
+status_t sinkMetadataToHal(const SinkMetadata& sinkMetadata,
+ std::vector<record_track_metadata>* halTracks) {
+ status_t result = NO_ERROR;
+ if (halTracks != nullptr) {
+ halTracks->reserve(sinkMetadata.tracks.size());
+ }
+ for (auto& metadata : sinkMetadata.tracks) {
+ record_track_metadata halTrackMetadata{.gain = metadata.gain};
+ CONVERT_CHECKED(HidlUtils::audioSourceToHal(metadata.source, &halTrackMetadata.source),
+ result);
+#if MAJOR_VERSION >= 5
+ if (metadata.destination.getDiscriminator() ==
+ RecordTrackMetadata::Destination::hidl_discriminator::device) {
+ CONVERT_CHECKED(
+ deviceAddressToHal(metadata.destination.device(), &halTrackMetadata.dest_device,
+ halTrackMetadata.dest_device_address),
+ result);
+ }
+#endif
+ if (halTracks != nullptr) {
+ halTracks->push_back(std::move(halTrackMetadata));
+ }
+ }
+ return result;
+}
+
+status_t sourceMetadataToHal(const SourceMetadata& sourceMetadata,
+ std::vector<playback_track_metadata_t>* halTracks) {
+ status_t result = NO_ERROR;
+ if (halTracks != nullptr) {
+ halTracks->reserve(sourceMetadata.tracks.size());
+ }
+ for (auto& metadata : sourceMetadata.tracks) {
+ playback_track_metadata_t halTrackMetadata{.gain = metadata.gain};
+ CONVERT_CHECKED(HidlUtils::audioUsageToHal(metadata.usage, &halTrackMetadata.usage),
+ result);
+ CONVERT_CHECKED(HidlUtils::audioContentTypeToHal(metadata.contentType,
+ &halTrackMetadata.content_type),
+ result);
+ if (halTracks != nullptr) {
+ halTracks->push_back(std::move(halTrackMetadata));
+ }
+ }
+ return result;
+}
+#endif // MAJOR_VERSION >= 4
+
+#if MAJOR_VERSION >= 7
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+bool audioInputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_input_flags_t* halFlags) {
+ bool success = true;
+ *halFlags = {};
+ for (const auto& flag : flags) {
+ audio_input_flags_t halFlag;
+ if (!xsd::isUnknownAudioInOutFlag(flag) &&
+ audio_input_flag_from_string(flag.c_str(), &halFlag)) {
+ *halFlags = static_cast<audio_input_flags_t>(*halFlags | halFlag);
+ } else {
+ ALOGE("Unknown audio input flag \"%s\"", flag.c_str());
+ success = false;
+ }
+ }
+ return success;
+}
+
+bool audioOutputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_output_flags_t* halFlags) {
+ bool success = true;
+ *halFlags = {};
+ for (const auto& flag : flags) {
+ audio_output_flags_t halFlag;
+ if (!xsd::isUnknownAudioInOutFlag(flag) &&
+ audio_output_flag_from_string(flag.c_str(), &halFlag)) {
+ *halFlags = static_cast<audio_output_flags_t>(*halFlags | halFlag);
+ } else {
+ ALOGE("Unknown audio output flag \"%s\"", flag.c_str());
+ success = false;
+ }
+ }
+ return success;
+}
+
+status_t sinkMetadataToHalV7(const SinkMetadata& sinkMetadata,
+ std::vector<record_track_metadata_v7_t>* halTracks) {
+ std::vector<record_track_metadata> bases;
+ status_t result = sinkMetadataToHal(sinkMetadata, halTracks != nullptr ? &bases : nullptr);
+ if (halTracks != nullptr) {
+ halTracks->reserve(bases.size());
+ }
+ auto baseIter = std::make_move_iterator(bases.begin());
+ for (auto& metadata : sinkMetadata.tracks) {
+ record_track_metadata_v7_t halTrackMetadata;
+ CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(metadata.channelMask,
+ &halTrackMetadata.channel_mask),
+ result);
+ CONVERT_CHECKED(HidlUtils::audioTagsToHal(metadata.tags, halTrackMetadata.tags), result);
+ if (halTracks != nullptr) {
+ halTrackMetadata.base = std::move(*baseIter++);
+ halTracks->push_back(std::move(halTrackMetadata));
+ }
+ }
+ return result;
+}
+
+status_t sourceMetadataToHalV7(const SourceMetadata& sourceMetadata,
+ std::vector<playback_track_metadata_v7_t>* halTracks) {
+ std::vector<playback_track_metadata_t> bases;
+ status_t result = sourceMetadataToHal(sourceMetadata, halTracks != nullptr ? &bases : nullptr);
+ if (halTracks != nullptr) {
+ halTracks->reserve(bases.size());
+ }
+ auto baseIter = std::make_move_iterator(bases.begin());
+ for (auto& metadata : sourceMetadata.tracks) {
+ playback_track_metadata_v7_t halTrackMetadata;
+ CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(metadata.channelMask,
+ &halTrackMetadata.channel_mask),
+ result);
+ CONVERT_CHECKED(HidlUtils::audioTagsToHal(metadata.tags, halTrackMetadata.tags), result);
+ if (halTracks != nullptr) {
+ halTrackMetadata.base = std::move(*baseIter++);
+ halTracks->push_back(std::move(halTrackMetadata));
+ }
+ }
+ return result;
+}
#endif
} // namespace implementation
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 6260ba1..05c1066 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -135,13 +135,14 @@
Return<void> Device::getInputBufferSize(const AudioConfig& config, getInputBufferSize_cb _hidl_cb) {
audio_config_t halConfig;
- HidlUtils::audioConfigToHal(config, &halConfig);
- size_t halBufferSize = mDevice->get_input_buffer_size(mDevice, &halConfig);
Result retval(Result::INVALID_ARGUMENTS);
uint64_t bufferSize = 0;
- if (halBufferSize != 0) {
- retval = Result::OK;
- bufferSize = halBufferSize;
+ if (HidlUtils::audioConfigToHal(config, &halConfig) == NO_ERROR) {
+ size_t halBufferSize = mDevice->get_input_buffer_size(mDevice, &halConfig);
+ if (halBufferSize != 0) {
+ retval = Result::OK;
+ bufferSize = halBufferSize;
+ }
}
_hidl_cb(retval, bufferSize);
return Void();
@@ -150,63 +151,80 @@
std::tuple<Result, sp<IStreamOut>> Device::openOutputStreamImpl(int32_t ioHandle,
const DeviceAddress& device,
const AudioConfig& config,
- AudioOutputFlagBitfield flags,
+ const AudioOutputFlags& flags,
AudioConfig* suggestedConfig) {
audio_config_t halConfig;
- HidlUtils::audioConfigToHal(config, &halConfig);
+ if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
+ return {Result::INVALID_ARGUMENTS, nullptr};
+ }
audio_stream_out_t* halStream;
- ALOGV(
- "open_output_stream handle: %d devices: %x flags: %#x "
- "srate: %d format %#x channels %x address %s",
- ioHandle, static_cast<audio_devices_t>(device.device),
- static_cast<audio_output_flags_t>(flags), halConfig.sample_rate, halConfig.format,
- halConfig.channel_mask, deviceAddressToHal(device).c_str());
- int status =
- mDevice->open_output_stream(mDevice, ioHandle, static_cast<audio_devices_t>(device.device),
- static_cast<audio_output_flags_t>(flags), &halConfig,
- &halStream, deviceAddressToHal(device).c_str());
+ audio_devices_t halDevice;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+ if (deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
+ return {Result::INVALID_ARGUMENTS, nullptr};
+ }
+ audio_output_flags_t halFlags;
+ if (!audioOutputFlagsToHal(flags, &halFlags)) {
+ return {Result::INVALID_ARGUMENTS, nullptr};
+ }
+ ALOGV("open_output_stream handle: %d devices: %x flags: %#x "
+ "srate: %d format %#x channels %x address %s",
+ ioHandle, halDevice, halFlags, halConfig.sample_rate, halConfig.format,
+ halConfig.channel_mask, halDeviceAddress);
+ int status = mDevice->open_output_stream(mDevice, ioHandle, halDevice, halFlags, &halConfig,
+ &halStream, halDeviceAddress);
ALOGV("open_output_stream status %d stream %p", status, halStream);
sp<IStreamOut> streamOut;
if (status == OK) {
streamOut = new StreamOut(this, halStream);
++mOpenedStreamsCount;
}
- status_t convertStatus = HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
+ status_t convertStatus =
+ HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, suggestedConfig);
ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
return {analyzeStatus("open_output_stream", status, {EINVAL} /*ignore*/), streamOut};
}
std::tuple<Result, sp<IStreamIn>> Device::openInputStreamImpl(
- int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
- AudioInputFlagBitfield flags, AudioSource source, AudioConfig* suggestedConfig) {
+ int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
+ const AudioInputFlags& flags, AudioSource source, AudioConfig* suggestedConfig) {
audio_config_t halConfig;
- HidlUtils::audioConfigToHal(config, &halConfig);
+ if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
+ return {Result::INVALID_ARGUMENTS, nullptr};
+ }
audio_stream_in_t* halStream;
- ALOGV(
- "open_input_stream handle: %d devices: %x flags: %#x "
- "srate: %d format %#x channels %x address %s source %d",
- ioHandle, static_cast<audio_devices_t>(device.device),
- static_cast<audio_input_flags_t>(flags), halConfig.sample_rate, halConfig.format,
- halConfig.channel_mask, deviceAddressToHal(device).c_str(),
- static_cast<audio_source_t>(source));
- int status = mDevice->open_input_stream(
- mDevice, ioHandle, static_cast<audio_devices_t>(device.device), &halConfig, &halStream,
- static_cast<audio_input_flags_t>(flags), deviceAddressToHal(device).c_str(),
- static_cast<audio_source_t>(source));
+ audio_devices_t halDevice;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+ if (deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
+ return {Result::INVALID_ARGUMENTS, nullptr};
+ }
+ audio_input_flags_t halFlags;
+ audio_source_t halSource;
+ if (!audioInputFlagsToHal(flags, &halFlags) ||
+ HidlUtils::audioSourceToHal(source, &halSource) != NO_ERROR) {
+ return {Result::INVALID_ARGUMENTS, nullptr};
+ }
+ ALOGV("open_input_stream handle: %d devices: %x flags: %#x "
+ "srate: %d format %#x channels %x address %s source %d",
+ ioHandle, halDevice, halFlags, halConfig.sample_rate, halConfig.format,
+ halConfig.channel_mask, halDeviceAddress, halSource);
+ int status = mDevice->open_input_stream(mDevice, ioHandle, halDevice, &halConfig, &halStream,
+ halFlags, halDeviceAddress, halSource);
ALOGV("open_input_stream status %d stream %p", status, halStream);
sp<IStreamIn> streamIn;
if (status == OK) {
streamIn = new StreamIn(this, halStream);
++mOpenedStreamsCount;
}
- status_t convertStatus = HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
+ status_t convertStatus =
+ HidlUtils::audioConfigFromHal(halConfig, true /*isInput*/, suggestedConfig);
ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
return {analyzeStatus("open_input_stream", status, {EINVAL} /*ignore*/), streamIn};
}
#if MAJOR_VERSION == 2
Return<void> Device::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioOutputFlagBitfield flags,
+ const AudioConfig& config, AudioOutputFlags flags,
openOutputStream_cb _hidl_cb) {
AudioConfig suggestedConfig;
auto [result, streamOut] =
@@ -216,7 +234,7 @@
}
Return<void> Device::openInputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioInputFlagBitfield flags,
+ const AudioConfig& config, AudioInputFlags flags,
AudioSource source, openInputStream_cb _hidl_cb) {
AudioConfig suggestedConfig;
auto [result, streamIn] =
@@ -227,9 +245,22 @@
#elif MAJOR_VERSION >= 4
Return<void> Device::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioOutputFlagBitfield flags,
+ const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+ AudioOutputFlags flags,
+#else
+ const AudioOutputFlags& flags,
+#endif
const SourceMetadata& sourceMetadata,
openOutputStream_cb _hidl_cb) {
+#if MAJOR_VERSION <= 6
+ if (status_t status = sourceMetadataToHal(sourceMetadata, nullptr); status != NO_ERROR) {
+#else
+ if (status_t status = sourceMetadataToHalV7(sourceMetadata, nullptr); status != NO_ERROR) {
+#endif
+ _hidl_cb(analyzeStatus("sourceMetadataToHal", status), nullptr, AudioConfig{});
+ return Void();
+ }
AudioConfig suggestedConfig;
auto [result, streamOut] =
openOutputStreamImpl(ioHandle, device, config, flags, &suggestedConfig);
@@ -241,14 +272,27 @@
}
Return<void> Device::openInputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioInputFlagBitfield flags,
+ const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+ AudioInputFlags flags,
+#else
+ const AudioInputFlags& flags,
+#endif
const SinkMetadata& sinkMetadata,
openInputStream_cb _hidl_cb) {
if (sinkMetadata.tracks.size() == 0) {
// This should never happen, the framework must not create as stream
// if there is no client
ALOGE("openInputStream called without tracks connected");
- _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, AudioConfig());
+ _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, AudioConfig{});
+ return Void();
+ }
+#if MAJOR_VERSION <= 6
+ if (status_t status = sinkMetadataToHal(sinkMetadata, nullptr); status != NO_ERROR) {
+#else
+ if (status_t status = sinkMetadataToHalV7(sinkMetadata, nullptr); status != NO_ERROR) {
+#endif
+ _hidl_cb(analyzeStatus("sinkMetadataToHal", status), nullptr, AudioConfig{});
return Void();
}
// Pick the first one as the main.
@@ -271,9 +315,7 @@
Return<void> Device::createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
const hidl_vec<AudioPortConfig>& sinks,
createAudioPatch_cb _hidl_cb) {
- auto [retval, patch] = createOrUpdateAudioPatch(
- static_cast<AudioPatchHandle>(AudioHandleConsts::AUDIO_PATCH_HANDLE_NONE), sources,
- sinks);
+ auto [retval, patch] = createOrUpdateAudioPatch(AudioPatchHandle{}, sources, sinks);
_hidl_cb(retval, patch);
return Void();
}
@@ -283,8 +325,10 @@
const hidl_vec<AudioPortConfig>& sinks) {
Result retval(Result::NOT_SUPPORTED);
if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
- std::unique_ptr<audio_port_config[]> halSources(HidlUtils::audioPortConfigsToHal(sources));
- std::unique_ptr<audio_port_config[]> halSinks(HidlUtils::audioPortConfigsToHal(sinks));
+ std::unique_ptr<audio_port_config[]> halSources;
+ HidlUtils::audioPortConfigsToHal(sources, &halSources);
+ std::unique_ptr<audio_port_config[]> halSinks;
+ HidlUtils::audioPortConfigsToHal(sinks, &halSinks);
audio_patch_handle_t halPatch = static_cast<audio_patch_handle_t>(patch);
retval = analyzeStatus("create_audio_patch",
mDevice->create_audio_patch(mDevice, sources.size(), &halSources[0],
@@ -452,7 +496,7 @@
const hidl_vec<AudioPortConfig>& sources,
const hidl_vec<AudioPortConfig>& sinks,
createAudioPatch_cb _hidl_cb) {
- if (previousPatch != static_cast<int32_t>(AudioHandleConsts::AUDIO_PATCH_HANDLE_NONE)) {
+ if (previousPatch != static_cast<int32_t>(AudioPatchHandle{})) {
auto [retval, patch] = createOrUpdateAudioPatch(previousPatch, sources, sinks);
_hidl_cb(retval, patch);
} else {
diff --git a/audio/core/all-versions/default/ParametersUtil.cpp b/audio/core/all-versions/default/ParametersUtil.cpp
index 0c8e28a..694eb73a 100644
--- a/audio/core/all-versions/default/ParametersUtil.cpp
+++ b/audio/core/all-versions/default/ParametersUtil.cpp
@@ -149,9 +149,15 @@
}
return setParams(params);
}
+
Result ParametersUtil::setParam(const char* name, const DeviceAddress& address) {
- AudioParameter params(String8(deviceAddressToHal(address).c_str()));
- params.addInt(String8(name), int(address.device));
+ audio_devices_t halDeviceType;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+ if (deviceAddressToHal(address, &halDeviceType, halDeviceAddress) != NO_ERROR) {
+ return Result::INVALID_ARGUMENTS;
+ }
+ AudioParameter params{String8(halDeviceAddress)};
+ params.addInt(String8(name), halDeviceType);
return setParams(params);
}
diff --git a/audio/core/all-versions/default/PrimaryDevice.cpp b/audio/core/all-versions/default/PrimaryDevice.cpp
index 11c1c5a..fe56177 100644
--- a/audio/core/all-versions/default/PrimaryDevice.cpp
+++ b/audio/core/all-versions/default/PrimaryDevice.cpp
@@ -73,28 +73,36 @@
#if MAJOR_VERSION == 2
Return<void> PrimaryDevice::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config,
- AudioOutputFlagBitfield flags,
+ const AudioConfig& config, AudioOutputFlags flags,
openOutputStream_cb _hidl_cb) {
return mDevice->openOutputStream(ioHandle, device, config, flags, _hidl_cb);
}
Return<void> PrimaryDevice::openInputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioInputFlagBitfield flags,
+ const AudioConfig& config, AudioInputFlags flags,
AudioSource source, openInputStream_cb _hidl_cb) {
return mDevice->openInputStream(ioHandle, device, config, flags, source, _hidl_cb);
}
#elif MAJOR_VERSION >= 4
Return<void> PrimaryDevice::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
const AudioConfig& config,
- AudioOutputFlagBitfield flags,
+#if MAJOR_VERSION <= 6
+ AudioOutputFlags flags,
+#else
+ const AudioOutputFlags& flags,
+#endif
const SourceMetadata& sourceMetadata,
openOutputStream_cb _hidl_cb) {
return mDevice->openOutputStream(ioHandle, device, config, flags, sourceMetadata, _hidl_cb);
}
Return<void> PrimaryDevice::openInputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioInputFlagBitfield flags,
+ const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+ AudioInputFlags flags,
+#else
+ const AudioInputFlags& flags,
+#endif
const SinkMetadata& sinkMetadata,
openInputStream_cb _hidl_cb) {
return mDevice->openInputStream(ioHandle, device, config, flags, sinkMetadata, _hidl_cb);
diff --git a/audio/core/all-versions/default/Stream.cpp b/audio/core/all-versions/default/Stream.cpp
index 74e5945..f964cbb 100644
--- a/audio/core/all-versions/default/Stream.cpp
+++ b/audio/core/all-versions/default/Stream.cpp
@@ -17,12 +17,14 @@
#define LOG_TAG "StreamHAL"
#include "core/default/Stream.h"
+#include "common/all-versions/HidlSupport.h"
#include "common/all-versions/default/EffectMap.h"
#include "core/default/Conversions.h"
#include "core/default/Util.h"
#include <inttypes.h>
+#include <HidlUtils.h>
#include <android/log.h>
#include <hardware/audio.h>
#include <hardware/audio_effect.h>
@@ -35,7 +37,12 @@
namespace CPP_VERSION {
namespace implementation {
-Stream::Stream(audio_stream_t* stream) : mStream(stream) {}
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::utils::splitString;
+
+Stream::Stream(bool isInput, audio_stream_t* stream) : mIsInput(isInput), mStream(stream) {
+ (void)mIsInput; // prevent 'unused field' warnings in pre-V7 versions.
+}
Stream::~Stream() {
mStream = nullptr;
@@ -78,6 +85,7 @@
return mStream->get_buffer_size(mStream);
}
+#if MAJOR_VERSION <= 6
Return<uint32_t> Stream::getSampleRate() {
return mStream->get_sample_rate(mStream);
}
@@ -201,6 +209,96 @@
return Void();
}
+#else // MAJOR_VERSION <= 6
+
+Return<void> Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
+ String8 halListValue;
+ Result result = getParam(AudioParameter::keyStreamSupportedFormats, &halListValue);
+ hidl_vec<AudioProfile> profiles;
+ if (result != Result::OK) {
+ _hidl_cb(result, profiles);
+ return Void();
+ }
+ // Ensure that the separator is one character, despite that it's defined as a C string.
+ static_assert(sizeof(AUDIO_PARAMETER_VALUE_LIST_SEPARATOR) == 2);
+ std::vector<std::string> halFormats =
+ splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
+ hidl_vec<AudioFormat> formats;
+ (void)HidlUtils::audioFormatsFromHal(halFormats, &formats);
+ std::vector<AudioProfile> tempProfiles;
+ for (const auto& format : formats) {
+ audio_format_t halFormat;
+ if (status_t status = HidlUtils::audioFormatToHal(format, &halFormat); status != NO_ERROR) {
+ continue;
+ }
+ AudioParameter context;
+ context.addInt(String8(AUDIO_PARAMETER_STREAM_FORMAT), int(halFormat));
+ // Query supported sample rates for the format.
+ result = getParam(AudioParameter::keyStreamSupportedSamplingRates, &halListValue, context);
+ if (result != Result::OK) break;
+ std::vector<std::string> halSampleRates =
+ splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
+ hidl_vec<uint32_t> sampleRates;
+ sampleRates.resize(halSampleRates.size());
+ for (size_t i = 0; i < sampleRates.size(); ++i) {
+ sampleRates[i] = std::stoi(halSampleRates[i]);
+ }
+ // Query supported channel masks for the format.
+ result = getParam(AudioParameter::keyStreamSupportedChannels, &halListValue, context);
+ if (result != Result::OK) break;
+ std::vector<std::string> halChannelMasks =
+ splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
+ hidl_vec<AudioChannelMask> channelMasks;
+ (void)HidlUtils::audioChannelMasksFromHal(halChannelMasks, &channelMasks);
+ // Create a profile.
+ if (channelMasks.size() != 0 && sampleRates.size() != 0) {
+ tempProfiles.push_back({.format = format,
+ .sampleRates = std::move(sampleRates),
+ .channelMasks = std::move(channelMasks)});
+ }
+ }
+ // Legacy get_parameter does not return a status_t, thus can not advertise of failure.
+ // Note that the method must not return an empty list if this capability is supported.
+ if (!tempProfiles.empty()) {
+ profiles = tempProfiles;
+ } else {
+ result = Result::NOT_SUPPORTED;
+ }
+ _hidl_cb(result, profiles);
+ return Void();
+}
+
+Return<void> Stream::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+ audio_config_base_t halConfigBase = {mStream->get_sample_rate(mStream),
+ mStream->get_channels(mStream),
+ mStream->get_format(mStream)};
+ AudioConfigBase configBase = {};
+ status_t status = HidlUtils::audioConfigBaseFromHal(halConfigBase, mIsInput, &configBase);
+ _hidl_cb(Stream::analyzeStatus("get_audio_properties", status), configBase);
+ return Void();
+}
+
+Return<Result> Stream::setAudioProperties(const AudioConfigBase& config) {
+ audio_config_base_t halConfigBase = {};
+ status_t status = HidlUtils::audioConfigBaseToHal(config, &halConfigBase);
+ if (status != NO_ERROR) {
+ return Stream::analyzeStatus("set_audio_properties", status);
+ }
+ if (Result result = setParam(AudioParameter::keySamplingRate,
+ static_cast<int>(halConfigBase.sample_rate));
+ result != Result::OK) {
+ return result;
+ }
+ if (Result result =
+ setParam(AudioParameter::keyChannels, static_cast<int>(halConfigBase.channel_mask));
+ result != Result::OK) {
+ return result;
+ }
+ return setParam(AudioParameter::keyFormat, static_cast<int>(halConfigBase.format));
+}
+
+#endif // MAJOR_VERSION <= 6
+
Return<Result> Stream::addEffect(uint64_t effectId) {
effect_handle_t halEffect = EffectMap::getInstance().get(effectId);
if (halEffect != NULL) {
@@ -257,12 +355,14 @@
}
#elif MAJOR_VERSION >= 4
Return<void> Stream::getDevices(getDevices_cb _hidl_cb) {
- int device = 0;
- Result retval = getParam(AudioParameter::keyRouting, &device);
+ int halDevice = 0;
+ Result retval = getParam(AudioParameter::keyRouting, &halDevice);
hidl_vec<DeviceAddress> devices;
if (retval == Result::OK) {
devices.resize(1);
- devices[0].device = static_cast<AudioDevice>(device);
+ retval = Stream::analyzeStatus("get_devices",
+ deviceAddressFromHal(static_cast<audio_devices_t>(halDevice),
+ nullptr, &devices[0]));
}
_hidl_cb(retval, devices);
return Void();
@@ -273,14 +373,13 @@
if (devices.size() > 1) {
return Result::NOT_SUPPORTED;
}
- DeviceAddress address;
+ DeviceAddress address{};
if (devices.size() == 1) {
address = devices[0];
- } else {
- address.device = AudioDevice::NONE;
}
return setParam(AudioParameter::keyRouting, address);
}
+
Return<void> Stream::getParameters(const hidl_vec<ParameterValue>& context,
const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
getParametersImpl(context, keys, _hidl_cb);
diff --git a/audio/core/all-versions/default/StreamIn.cpp b/audio/core/all-versions/default/StreamIn.cpp
index f1152ca..2c5e9f1 100644
--- a/audio/core/all-versions/default/StreamIn.cpp
+++ b/audio/core/all-versions/default/StreamIn.cpp
@@ -24,11 +24,12 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <HidlUtils.h>
#include <android/log.h>
#include <hardware/audio.h>
#include <utils/Trace.h>
-#include <memory>
#include <cmath>
+#include <memory>
namespace android {
namespace hardware {
@@ -36,6 +37,8 @@
namespace CPP_VERSION {
namespace implementation {
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+
namespace {
class ReadThread : public Thread {
@@ -141,7 +144,7 @@
StreamIn::StreamIn(const sp<Device>& device, audio_stream_in_t* stream)
: mDevice(device),
mStream(stream),
- mStreamCommon(new Stream(&stream->common)),
+ mStreamCommon(new Stream(true /*isInput*/, &stream->common)),
mStreamMmap(new StreamMmap<audio_stream_in_t>(stream)),
mEfGroup(nullptr),
mStopReadThread(false) {}
@@ -177,6 +180,7 @@
return mStreamCommon->getBufferSize();
}
+#if MAJOR_VERSION <= 6
Return<uint32_t> StreamIn::getSampleRate() {
return mStreamCommon->getSampleRate();
}
@@ -223,6 +227,18 @@
return mStreamCommon->setFormat(format);
}
+#else
+
+Return<void> StreamIn::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
+ return mStreamCommon->getSupportedProfiles(_hidl_cb);
+}
+
+Return<Result> StreamIn::setAudioProperties(const AudioConfigBase& config) {
+ return mStreamCommon->setAudioProperties(config);
+}
+
+#endif // MAJOR_VERSION <= 6
+
Return<void> StreamIn::getAudioProperties(getAudioProperties_cb _hidl_cb) {
return mStreamCommon->getAudioProperties(_hidl_cb);
}
@@ -321,9 +337,11 @@
Return<void> StreamIn::getAudioSource(getAudioSource_cb _hidl_cb) {
int halSource;
Result retval = mStreamCommon->getParam(AudioParameter::keyInputSource, &halSource);
- AudioSource source(AudioSource::DEFAULT);
+ AudioSource source = {};
if (retval == Result::OK) {
- source = AudioSource(halSource);
+ retval = Stream::analyzeStatus(
+ "get_audio_source",
+ HidlUtils::audioSourceFromHal(static_cast<audio_source_t>(halSource), &source));
}
_hidl_cb(retval, source);
return Void();
@@ -340,7 +358,11 @@
Return<void> StreamIn::prepareForReading(uint32_t frameSize, uint32_t framesCount,
prepareForReading_cb _hidl_cb) {
status_t status;
+#if MAJOR_VERSION <= 6
ThreadInfo threadInfo = {0, 0};
+#else
+ int32_t threadInfo = 0;
+#endif
// Wrap the _hidl_cb to return an error
auto sendError = [&threadInfo, &_hidl_cb](Result result) {
@@ -410,8 +432,12 @@
mStatusMQ = std::move(tempStatusMQ);
mReadThread = tempReadThread.release();
mEfGroup = tempElfGroup.release();
+#if MAJOR_VERSION <= 6
threadInfo.pid = getpid();
threadInfo.tid = mReadThread->getTid();
+#else
+ threadInfo = mReadThread->getTid();
+#endif
_hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(),
threadInfo);
return Void();
@@ -452,34 +478,70 @@
}
#if MAJOR_VERSION >= 4
-Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
- if (mStream->update_sink_metadata == nullptr) {
- return Void(); // not supported by the HAL
- }
+Result StreamIn::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata) {
std::vector<record_track_metadata> halTracks;
- halTracks.reserve(sinkMetadata.tracks.size());
- for (auto& metadata : sinkMetadata.tracks) {
- record_track_metadata halTrackMetadata = {
- .source = static_cast<audio_source_t>(metadata.source), .gain = metadata.gain};
-#if MAJOR_VERSION >= 5
- if (metadata.destination.getDiscriminator() ==
- RecordTrackMetadata::Destination::hidl_discriminator::device) {
- halTrackMetadata.dest_device =
- static_cast<audio_devices_t>(metadata.destination.device().device);
- strncpy(halTrackMetadata.dest_device_address,
- deviceAddressToHal(metadata.destination.device()).c_str(),
- AUDIO_DEVICE_MAX_ADDRESS_LEN);
+#if MAJOR_VERSION <= 6
+ (void)sinkMetadataToHal(sinkMetadata, &halTracks);
+#else
+ // Validate whether a conversion to V7 is possible. This is needed
+ // to have a consistent behavior of the HAL regardless of the API
+ // version of the legacy HAL (and also to be consistent with openInputStream).
+ std::vector<record_track_metadata_v7> halTracksV7;
+ if (status_t status = sinkMetadataToHalV7(sinkMetadata, &halTracksV7); status == NO_ERROR) {
+ halTracks.reserve(halTracksV7.size());
+ for (auto metadata_v7 : halTracksV7) {
+ halTracks.push_back(std::move(metadata_v7.base));
}
-#endif
- halTracks.push_back(halTrackMetadata);
+ } else {
+ return Stream::analyzeStatus("sinkMetadataToHal", status);
}
+#endif // MAJOR_VERSION <= 6
const sink_metadata_t halMetadata = {
.track_count = halTracks.size(),
.tracks = halTracks.data(),
};
mStream->update_sink_metadata(mStream, &halMetadata);
+ return Result::OK;
+}
+
+#if MAJOR_VERSION >= 7
+Result StreamIn::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) {
+ std::vector<record_track_metadata_v7> halTracks;
+ if (status_t status = sinkMetadataToHalV7(sinkMetadata, &halTracks); status != NO_ERROR) {
+ return Stream::analyzeStatus("sinkMetadataToHal", status);
+ }
+ const sink_metadata_v7_t halMetadata = {
+ .track_count = halTracks.size(),
+ .tracks = halTracks.data(),
+ };
+ mStream->update_sink_metadata_v7(mStream, &halMetadata);
+ return Result::OK;
+}
+#endif // MAJOR_VERSION >= 7
+
+#if MAJOR_VERSION <= 6
+Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
+ if (mStream->update_sink_metadata == nullptr) {
+ return Void(); // not supported by the HAL
+ }
+ (void)doUpdateSinkMetadata(sinkMetadata);
return Void();
}
+#elif MAJOR_VERSION >= 7
+Return<Result> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
+ if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
+ if (mStream->update_sink_metadata == nullptr) {
+ return Result::NOT_SUPPORTED;
+ }
+ return doUpdateSinkMetadata(sinkMetadata);
+ } else {
+ if (mStream->update_sink_metadata_v7 == nullptr) {
+ return Result::NOT_SUPPORTED;
+ }
+ return doUpdateSinkMetadataV7(sinkMetadata);
+ }
+}
+#endif
Return<void> StreamIn::getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) {
Result retval = Result::NOT_SUPPORTED;
diff --git a/audio/core/all-versions/default/StreamOut.cpp b/audio/core/all-versions/default/StreamOut.cpp
index 007eb45..ffd3b6b 100644
--- a/audio/core/all-versions/default/StreamOut.cpp
+++ b/audio/core/all-versions/default/StreamOut.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "StreamOutHAL"
#include "core/default/StreamOut.h"
+#include "core/default/Conversions.h"
#include "core/default/Util.h"
//#define LOG_NDEBUG 0
@@ -26,6 +27,7 @@
#include <memory>
+#include <HidlUtils.h>
#include <android/log.h>
#include <hardware/audio.h>
#include <utils/Trace.h>
@@ -36,6 +38,8 @@
namespace CPP_VERSION {
namespace implementation {
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+
namespace {
class WriteThread : public Thread {
@@ -142,7 +146,7 @@
StreamOut::StreamOut(const sp<Device>& device, audio_stream_out_t* stream)
: mDevice(device),
mStream(stream),
- mStreamCommon(new Stream(&stream->common)),
+ mStreamCommon(new Stream(false /*isInput*/, &stream->common)),
mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)),
mEfGroup(nullptr),
mStopWriteThread(false) {}
@@ -182,6 +186,7 @@
return mStreamCommon->getBufferSize();
}
+#if MAJOR_VERSION <= 6
Return<uint32_t> StreamOut::getSampleRate() {
return mStreamCommon->getSampleRate();
}
@@ -228,6 +233,18 @@
return mStreamCommon->setFormat(format);
}
+#else
+
+Return<void> StreamOut::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
+ return mStreamCommon->getSupportedProfiles(_hidl_cb);
+}
+
+Return<Result> StreamOut::setAudioProperties(const AudioConfigBase& config) {
+ return mStreamCommon->setAudioProperties(config);
+}
+
+#endif // MAJOR_VERSION <= 6
+
Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) {
return mStreamCommon->getAudioProperties(_hidl_cb);
}
@@ -327,7 +344,11 @@
Return<void> StreamOut::prepareForWriting(uint32_t frameSize, uint32_t framesCount,
prepareForWriting_cb _hidl_cb) {
status_t status;
+#if MAJOR_VERSION <= 6
ThreadInfo threadInfo = {0, 0};
+#else
+ int32_t threadInfo = 0;
+#endif
// Wrap the _hidl_cb to return an error
auto sendError = [&threadInfo, &_hidl_cb](Result result) {
@@ -396,8 +417,12 @@
mStatusMQ = std::move(tempStatusMQ);
mWriteThread = tempWriteThread.release();
mEfGroup = tempElfGroup.release();
+#if MAJOR_VERSION <= 6
threadInfo.pid = getpid();
threadInfo.tid = mWriteThread->getTid();
+#else
+ threadInfo = mWriteThread->getTid();
+#endif
_hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(),
threadInfo);
return Void();
@@ -561,26 +586,71 @@
}
#if MAJOR_VERSION >= 4
-Return<void> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
- if (mStream->update_source_metadata == nullptr) {
- return Void(); // not supported by the HAL
+Result StreamOut::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata) {
+ std::vector<playback_track_metadata_t> halTracks;
+#if MAJOR_VERSION <= 6
+ (void)sourceMetadataToHal(sourceMetadata, &halTracks);
+#else
+ // Validate whether a conversion to V7 is possible. This is needed
+ // to have a consistent behavior of the HAL regardless of the API
+ // version of the legacy HAL (and also to be consistent with openOutputStream).
+ std::vector<playback_track_metadata_v7> halTracksV7;
+ if (status_t status = sourceMetadataToHalV7(sourceMetadata, &halTracksV7); status == NO_ERROR) {
+ halTracks.reserve(halTracksV7.size());
+ for (auto metadata_v7 : halTracksV7) {
+ halTracks.push_back(std::move(metadata_v7.base));
+ }
+ } else {
+ return Stream::analyzeStatus("sourceMetadataToHal", status);
}
- std::vector<playback_track_metadata> halTracks;
- halTracks.reserve(sourceMetadata.tracks.size());
- for (auto& metadata : sourceMetadata.tracks) {
- halTracks.push_back({
- .usage = static_cast<audio_usage_t>(metadata.usage),
- .content_type = static_cast<audio_content_type_t>(metadata.contentType),
- .gain = metadata.gain,
- });
- }
+#endif // MAJOR_VERSION <= 6
const source_metadata_t halMetadata = {
.track_count = halTracks.size(),
.tracks = halTracks.data(),
};
mStream->update_source_metadata(mStream, &halMetadata);
+ return Result::OK;
+}
+
+#if MAJOR_VERSION >= 7
+Result StreamOut::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) {
+ std::vector<playback_track_metadata_v7> halTracks;
+ if (status_t status = sourceMetadataToHalV7(sourceMetadata, &halTracks); status != NO_ERROR) {
+ return Stream::analyzeStatus("sourceMetadataToHal", status);
+ }
+ const source_metadata_v7_t halMetadata = {
+ .track_count = halTracks.size(),
+ .tracks = halTracks.data(),
+ };
+ mStream->update_source_metadata_v7(mStream, &halMetadata);
+ return Result::OK;
+}
+#endif // MAJOR_VERSION >= 7
+
+#if MAJOR_VERSION <= 6
+Return<void> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
+ if (mStream->update_source_metadata == nullptr) {
+ return Void(); // not supported by the HAL
+ }
+ (void)doUpdateSourceMetadata(sourceMetadata);
return Void();
}
+#elif MAJOR_VERSION >= 7
+Return<Result> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
+ if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
+ if (mStream->update_source_metadata == nullptr) {
+ return Result::NOT_SUPPORTED;
+ }
+ return doUpdateSourceMetadata(sourceMetadata);
+ } else {
+ if (mStream->update_source_metadata_v7 == nullptr) {
+ return Result::NOT_SUPPORTED;
+ }
+ return doUpdateSourceMetadataV7(sourceMetadata);
+ }
+}
+#endif
+
Return<Result> StreamOut::selectPresentation(int32_t /*presentationId*/, int32_t /*programId*/) {
return Result::NOT_SUPPORTED; // TODO: propagate to legacy
}
diff --git a/audio/core/all-versions/default/include/core/default/Conversions.h b/audio/core/all-versions/default/include/core/default/Conversions.h
index cb7914f..61720d5 100644
--- a/audio/core/all-versions/default/include/core/default/Conversions.h
+++ b/audio/core/all-versions/default/include/core/default/Conversions.h
@@ -23,20 +23,55 @@
#include <system/audio.h>
+#include <VersionUtils.h>
+
namespace android {
namespace hardware {
namespace audio {
namespace CPP_VERSION {
namespace implementation {
+using ::android::hardware::hidl_vec;
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::CPP_VERSION;
-std::string deviceAddressToHal(const DeviceAddress& address);
+status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+ char* halDeviceAddress);
+status_t deviceAddressFromHal(audio_devices_t halDeviceType, const char* halDeviceAddress,
+ DeviceAddress* device);
#if MAJOR_VERSION >= 4
bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst,
const struct audio_microphone_characteristic_t& src);
+status_t sinkMetadataToHal(const SinkMetadata& sinkMetadata,
+ std::vector<record_track_metadata>* halTracks);
+status_t sourceMetadataToHal(const SourceMetadata& sourceMetadata,
+ std::vector<playback_track_metadata_t>* halTracks);
+#endif
+
+#if MAJOR_VERSION <= 6
+using AudioInputFlags =
+ ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
+using AudioOutputFlags =
+ ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
+
+inline bool audioInputFlagsToHal(AudioInputFlags flags, audio_input_flags_t* halFlags) {
+ *halFlags = static_cast<audio_input_flags_t>(flags);
+ return true;
+}
+
+inline bool audioOutputFlagsToHal(AudioOutputFlags flags, audio_output_flags_t* halFlags) {
+ *halFlags = static_cast<audio_output_flags_t>(flags);
+ return true;
+}
+#else
+bool audioInputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_input_flags_t* halFlags);
+bool audioOutputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_output_flags_t* halFlags);
+// Overloading isn't convenient when passing a nullptr.
+status_t sinkMetadataToHalV7(const SinkMetadata& sinkMetadata,
+ std::vector<record_track_metadata_v7_t>* halTracks);
+status_t sourceMetadataToHalV7(const SourceMetadata& sourceMetadata,
+ std::vector<playback_track_metadata_v7_t>* halTracks);
#endif
} // namespace implementation
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 907acd7..2a4d226 100644
--- a/audio/core/all-versions/default/include/core/default/Device.h
+++ b/audio/core/all-versions/default/include/core/default/Device.h
@@ -44,8 +44,13 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
#if MAJOR_VERSION <= 6
-using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
-using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
+using AudioInputFlags =
+ ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
+using AudioOutputFlags =
+ ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
+#else
+using AudioInputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
+using AudioOutputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
#endif
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::CPP_VERSION;
@@ -67,28 +72,36 @@
std::tuple<Result, sp<IStreamOut>> openOutputStreamImpl(int32_t ioHandle,
const DeviceAddress& device,
const AudioConfig& config,
- AudioOutputFlagBitfield flags,
+ const AudioOutputFlags& flags,
AudioConfig* suggestedConfig);
std::tuple<Result, sp<IStreamIn>> openInputStreamImpl(
- int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
- AudioInputFlagBitfield flags, AudioSource source, AudioConfig* suggestedConfig);
-#if MAJOR_VERSION == 2
+ int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
+ const AudioInputFlags& flags, AudioSource source, AudioConfig* suggestedConfig);
+
Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioOutputFlagBitfield flags,
- openOutputStream_cb _hidl_cb) override;
- Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioInputFlagBitfield flags,
- AudioSource source, openInputStream_cb _hidl_cb) override;
-#elif MAJOR_VERSION >= 4
- Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioOutputFlagBitfield flags,
- const SourceMetadata& sourceMetadata,
- openOutputStream_cb _hidl_cb) override;
- Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioInputFlagBitfield flags,
- const SinkMetadata& sinkMetadata,
- openInputStream_cb _hidl_cb) override;
+ const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+ AudioOutputFlags flags,
+#else
+ const AudioOutputFlags& flags,
#endif
+#if MAJOR_VERSION >= 4
+ const SourceMetadata& sourceMetadata,
+#endif
+ openOutputStream_cb _hidl_cb) override;
+ Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
+ const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+ AudioInputFlags flags,
+#else
+ const AudioInputFlags& flags,
+#endif
+#if MAJOR_VERSION == 2
+ AudioSource source,
+#elif MAJOR_VERSION >= 4
+ const SinkMetadata& sinkMetadata,
+#endif
+ openInputStream_cb _hidl_cb) override;
Return<bool> supportsAudioPatches() override;
Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
@@ -133,6 +146,8 @@
void closeOutputStream(audio_stream_out_t* stream);
audio_hw_device_t* device() const { return mDevice; }
+ uint32_t version() const { return mDevice->common.version; }
+
private:
bool mIsClosed;
audio_hw_device_t* mDevice;
@@ -148,8 +163,6 @@
// Methods from ParametersUtil.
char* halGetParameters(const char* keys) override;
int halSetParameters(const char* keysAndValues) override;
-
- uint32_t version() const { return mDevice->common.version; }
};
} // namespace implementation
diff --git a/audio/core/all-versions/default/include/core/default/PrimaryDevice.h b/audio/core/all-versions/default/include/core/default/PrimaryDevice.h
index ccdb7b2..5f65acf 100644
--- a/audio/core/all-versions/default/include/core/default/PrimaryDevice.h
+++ b/audio/core/all-versions/default/include/core/default/PrimaryDevice.h
@@ -54,21 +54,29 @@
getInputBufferSize_cb _hidl_cb) override;
Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioOutputFlagBitfield flags,
+ const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+ AudioOutputFlags flags,
+#else
+ const AudioOutputFlags& flags,
+#endif
#if MAJOR_VERSION >= 4
const SourceMetadata& sourceMetadata,
#endif
openOutputStream_cb _hidl_cb) override;
-
Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioInputFlagBitfield flags,
- AudioSource source, openInputStream_cb _hidl_cb);
-#if MAJOR_VERSION >= 4
- Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
- const AudioConfig& config, AudioInputFlagBitfield flags,
- const SinkMetadata& sinkMetadata,
- openInputStream_cb _hidl_cb) override;
+ const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+ AudioInputFlags flags,
+#else
+ const AudioInputFlags& flags,
#endif
+#if MAJOR_VERSION == 2
+ AudioSource source,
+#elif MAJOR_VERSION >= 4
+ const SinkMetadata& sinkMetadata,
+#endif
+ openInputStream_cb _hidl_cb) override;
Return<bool> supportsAudioPatches() override;
Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
diff --git a/audio/core/all-versions/default/include/core/default/Stream.h b/audio/core/all-versions/default/include/core/default/Stream.h
index ce0003b..0865992 100644
--- a/audio/core/all-versions/default/include/core/default/Stream.h
+++ b/audio/core/all-versions/default/include/core/default/Stream.h
@@ -41,12 +41,14 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
+#if MAJOR_VERSION <= 6
using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioChannelBitfield;
+#endif
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::CPP_VERSION;
struct Stream : public IStream, public ParametersUtil {
- explicit Stream(audio_stream_t* stream);
+ Stream(bool isInput, audio_stream_t* stream);
/** 1GiB is the maximum buffer size the HAL client is allowed to request.
* This value has been chosen to be under SIZE_MAX and still big enough
@@ -59,6 +61,7 @@
Return<uint64_t> getFrameSize() override;
Return<uint64_t> getFrameCount() override;
Return<uint64_t> getBufferSize() override;
+#if MAJOR_VERSION <= 6
Return<uint32_t> getSampleRate() override;
#if MAJOR_VERSION == 2
Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
@@ -72,6 +75,10 @@
Return<AudioFormat> getFormat() override;
Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
Return<Result> setFormat(AudioFormat format) override;
+#else
+ Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
+ Return<Result> setAudioProperties(const AudioConfigBase& config) override;
+#endif // MAJOR_VERSION <= 6
Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
Return<Result> addEffect(uint64_t effectId) override;
Return<Result> removeEffect(uint64_t effectId) override;
@@ -110,13 +117,14 @@
const std::vector<int>& ignoreErrors);
private:
- audio_stream_t* mStream;
+ const bool mIsInput;
+ audio_stream_t* mStream;
- virtual ~Stream();
+ virtual ~Stream();
- // Methods from ParametersUtil.
- char* halGetParameters(const char* keys) override;
- int halSetParameters(const char* keysAndValues) override;
+ // Methods from ParametersUtil.
+ char* halGetParameters(const char* keys) override;
+ int halSetParameters(const char* keysAndValues) override;
};
template <typename T>
diff --git a/audio/core/all-versions/default/include/core/default/StreamIn.h b/audio/core/all-versions/default/include/core/default/StreamIn.h
index 24f9944..651b3a6 100644
--- a/audio/core/all-versions/default/include/core/default/StreamIn.h
+++ b/audio/core/all-versions/default/include/core/default/StreamIn.h
@@ -56,6 +56,7 @@
Return<uint64_t> getFrameSize() override;
Return<uint64_t> getFrameCount() override;
Return<uint64_t> getBufferSize() override;
+#if MAJOR_VERSION <= 6
Return<uint32_t> getSampleRate() override;
#if MAJOR_VERSION == 2
Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
@@ -69,6 +70,10 @@
Return<AudioFormat> getFormat() override;
Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
Return<Result> setFormat(AudioFormat format) override;
+#else
+ Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
+ Return<Result> setAudioProperties(const AudioConfigBase& config) override;
+#endif // MAJOR_VERSION <= 6
Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
Return<Result> addEffect(uint64_t effectId) override;
Return<Result> removeEffect(uint64_t effectId) override;
@@ -109,9 +114,13 @@
Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
#if MAJOR_VERSION >= 4
+#if MAJOR_VERSION <= 6
Return<void> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
- Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
+#else
+ Return<Result> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
#endif
+ Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
+#endif // MAJOR_VERSION >= 4
#if MAJOR_VERSION >= 5
Return<Result> setMicrophoneDirection(MicrophoneDirection direction) override;
Return<Result> setMicrophoneFieldDimension(float zoom) override;
@@ -119,7 +128,14 @@
static Result getCapturePositionImpl(audio_stream_in_t* stream, uint64_t* frames,
uint64_t* time);
- private:
+ private:
+#if MAJOR_VERSION >= 4
+ Result doUpdateSinkMetadata(const SinkMetadata& sinkMetadata);
+#if MAJOR_VERSION >= 7
+ Result doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata);
+#endif
+#endif // MAJOR_VERSION >= 4
+
const sp<Device> mDevice;
audio_stream_in_t* mStream;
const sp<Stream> mStreamCommon;
diff --git a/audio/core/all-versions/default/include/core/default/StreamOut.h b/audio/core/all-versions/default/include/core/default/StreamOut.h
index e647da9..b8e8515 100644
--- a/audio/core/all-versions/default/include/core/default/StreamOut.h
+++ b/audio/core/all-versions/default/include/core/default/StreamOut.h
@@ -56,6 +56,7 @@
Return<uint64_t> getFrameSize() override;
Return<uint64_t> getFrameCount() override;
Return<uint64_t> getBufferSize() override;
+#if MAJOR_VERSION <= 6
Return<uint32_t> getSampleRate() override;
#if MAJOR_VERSION == 2
Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
@@ -69,6 +70,10 @@
Return<AudioFormat> getFormat() override;
Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
Return<Result> setFormat(AudioFormat format) override;
+#else
+ Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
+ Return<Result> setAudioProperties(const AudioConfigBase& config) override;
+#endif // MAJOR_VERSION <= 6
Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
Return<Result> addEffect(uint64_t effectId) override;
Return<Result> removeEffect(uint64_t effectId) override;
@@ -118,9 +123,13 @@
Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
#if MAJOR_VERSION >= 4
- Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
Return<Result> selectPresentation(int32_t presentationId, int32_t programId) override;
+#if MAJOR_VERSION <= 6
+ Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
+#else
+ Return<Result> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
#endif
+#endif // MAJOR_VERSION >= 4
#if MAJOR_VERSION >= 6
Return<void> getDualMonoMode(getDualMonoMode_cb _hidl_cb) override;
Return<Result> setDualMonoMode(DualMonoMode mode) override;
@@ -138,6 +147,13 @@
#endif
private:
+#if MAJOR_VERSION >= 4
+ Result doUpdateSourceMetadata(const SourceMetadata& sourceMetadata);
+#if MAJOR_VERSION >= 7
+ Result doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata);
+#endif
+#endif // MAJOR_VERSION >= 4
+
const sp<Device> mDevice;
audio_stream_out_t* mStream;
const sp<Stream> mStreamCommon;
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 eb8cb3f..5076dcf 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -67,13 +67,15 @@
auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
const SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}};
#elif MAJOR_VERSION >= 7
- config.base.channelMask.resize(1);
- config.base.channelMask[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO);
+ config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO);
config.base.sampleRateHz = 8000;
config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
hidl_vec<hidl_string> flags;
const SinkMetadata initMetadata = {
- {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_MIC), .gain = 1}}};
+ {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_MIC),
+ .gain = 1,
+ .tags = {},
+ .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
#endif
EventFlag* efGroup;
for (auto microphone : microphones) {
@@ -141,20 +143,23 @@
#if MAJOR_VERSION <= 6
using AD = AudioDevice;
for (auto deviceType : {AD::OUT_HDMI, AD::OUT_WIRED_HEADPHONE, AD::IN_USB_HEADSET}) {
+ SCOPED_TRACE("device=" + ::testing::PrintToString(deviceType));
#elif MAJOR_VERSION >= 7
using AD = xsd::AudioDevice;
- for (auto deviceType :
- {toString(AD::AUDIO_DEVICE_OUT_HDMI), toString(AD::AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
- toString(AD::AUDIO_DEVICE_IN_USB_HEADSET)}) {
+ for (auto deviceType : {AD::AUDIO_DEVICE_OUT_HDMI, AD::AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
+ AD::AUDIO_DEVICE_IN_USB_HEADSET}) {
+ SCOPED_TRACE("device=" + toString(deviceType));
#endif
- SCOPED_TRACE("device=" + ::testing::PrintToString(deviceType));
for (bool state : {true, false}) {
SCOPED_TRACE("state=" + ::testing::PrintToString(state));
DeviceAddress address = {};
#if MAJOR_VERSION <= 6
address.device = deviceType;
#elif MAJOR_VERSION >= 7
- address.deviceType = deviceType;
+ address.deviceType = toString(deviceType);
+ if (deviceType == AD::AUDIO_DEVICE_IN_USB_HEADSET) {
+ address.address.alsa({0, 0});
+ }
#endif
auto ret = getDevice()->setConnectedState(address, state);
ASSERT_TRUE(ret.isOk());
@@ -232,22 +237,14 @@
TEST_P(InputStreamTest, updateSinkMetadata) {
doc::test("The HAL should not crash on metadata change");
-
#if MAJOR_VERSION <= 6
hidl_enum_range<AudioSource> range;
-#elif MAJOR_VERSION >= 7
- xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioSource> range;
-#endif
// Test all possible track configuration
for (auto source : range) {
for (float volume : {0.0, 0.5, 1.0}) {
-#if MAJOR_VERSION <= 6
const SinkMetadata metadata = {{{.source = source, .gain = volume}}};
-#elif MAJOR_VERSION >= 7
- const SinkMetadata metadata = {{{.source = toString(source), .gain = volume}}};
-#endif
ASSERT_OK(stream->updateSinkMetadata(metadata))
- << "source=" << toString(source) << ", volume=" << volume;
+ << "source=" << toString(source) << ", volume=" << volume;
}
}
@@ -255,9 +252,30 @@
// Set no metadata as if all stream track had stopped
ASSERT_OK(stream->updateSinkMetadata({}));
-
// Restore initial
ASSERT_OK(stream->updateSinkMetadata(initMetadata));
+
+#elif MAJOR_VERSION >= 7
+ xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioSource> range;
+ // Test all possible track configuration
+ for (auto source : range) {
+ for (float volume : {0.0, 0.5, 1.0}) {
+ const SinkMetadata metadata = {
+ {{.source = toString(source),
+ .gain = volume,
+ .tags = {},
+ .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
+ ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata(metadata))
+ << "source=" << toString(source) << ", volume=" << volume;
+ }
+ }
+ // Do not test concurrent capture as this is not officially supported
+
+ // Set no metadata as if all stream track had stopped
+ ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata({}));
+ // Restore initial
+ ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata(initMetadata));
+#endif
}
TEST_P(OutputStreamTest, SelectPresentation) {
@@ -267,56 +285,82 @@
TEST_P(OutputStreamTest, updateSourceMetadata) {
doc::test("The HAL should not crash on metadata change");
-
#if MAJOR_VERSION <= 6
hidl_enum_range<AudioUsage> usageRange;
hidl_enum_range<AudioContentType> contentRange;
-#elif MAJOR_VERSION >= 7
- xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioUsage> usageRange;
- xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioContentType> contentRange;
-#endif
// Test all possible track configuration
for (auto usage : usageRange) {
for (auto content : contentRange) {
for (float volume : {0.0, 0.5, 1.0}) {
-#if MAJOR_VERSION <= 6
const SourceMetadata metadata = {{{usage, content, volume}}};
-#elif MAJOR_VERSION >= 7
- const SourceMetadata metadata = {{{toString(usage), toString(content), volume}}};
-#endif
ASSERT_OK(stream->updateSourceMetadata(metadata))
<< "usage=" << toString(usage) << ", content=" << toString(content)
<< ", volume=" << volume;
}
}
}
-
- // clang-format off
// Set many track of different configuration
+ // clang-format off
ASSERT_OK(stream->updateSourceMetadata(
-#if MAJOR_VERSION <= 6
{{{AudioUsage::MEDIA, AudioContentType::MUSIC, 0.1},
{AudioUsage::VOICE_COMMUNICATION, AudioContentType::SPEECH, 1.0},
{AudioUsage::ALARM, AudioContentType::SONIFICATION, 0.0},
{AudioUsage::ASSISTANT, AudioContentType::UNKNOWN, 0.3}}}
-#elif MAJOR_VERSION >= 7
- {{{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
- toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC), 0.1},
- {toString(xsd::AudioUsage::AUDIO_USAGE_VOICE_COMMUNICATION),
- toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SPEECH), 1.0},
- {toString(xsd::AudioUsage::AUDIO_USAGE_ALARM),
- toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SONIFICATION), 0.0},
- {toString(xsd::AudioUsage::AUDIO_USAGE_ASSISTANT),
- toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN), 0.3}}}
-#endif
));
// clang-format on
-
// Set no metadata as if all stream track had stopped
ASSERT_OK(stream->updateSourceMetadata({}));
-
// Restore initial
ASSERT_OK(stream->updateSourceMetadata(initMetadata));
+#elif MAJOR_VERSION >= 7
+ xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioUsage> usageRange;
+ xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioContentType> contentRange;
+ // Test all possible track configuration
+ for (auto usage : usageRange) {
+ for (auto content : contentRange) {
+ for (float volume : {0.0, 0.5, 1.0}) {
+ const SourceMetadata metadata = {
+ {{toString(usage),
+ toString(content),
+ {} /* tags */,
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
+ volume}}};
+ ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(metadata))
+ << "usage=" << toString(usage) << ", content=" << toString(content)
+ << ", volume=" << volume;
+ }
+ }
+ }
+ // Set many track of different configuration
+ // clang-format off
+ ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(
+ {{{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+ toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+ {},
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
+ 0.1},
+ {toString(xsd::AudioUsage::AUDIO_USAGE_VOICE_COMMUNICATION),
+ toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SPEECH),
+ {}, // tags
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
+ 1.0},
+ {toString(xsd::AudioUsage::AUDIO_USAGE_ALARM),
+ toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SONIFICATION),
+ {}, // tags
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
+ 0.0},
+ {toString(xsd::AudioUsage::AUDIO_USAGE_ASSISTANT),
+ toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN),
+ {},
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
+ 0.3}}}
+ ));
+ // clang-format on
+ // Set no metadata as if all stream track had stopped
+ ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata({}));
+ // Restore initial
+ ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(initMetadata));
+#endif
}
TEST_P(AudioPrimaryHidlTest, setMode) {
diff --git a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
index bd8de2d..94fa92a 100644
--- a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
@@ -18,92 +18,120 @@
#include "5.0/AudioPrimaryHidlHalTest.cpp"
#if MAJOR_VERSION <= 6
-const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
- static std::vector<DeviceConfigParameter> parameters = [] {
- std::vector<DeviceConfigParameter> result;
- for (const auto& device : getDeviceParameters()) {
- auto module =
- getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
- for (const auto& ioProfile : module->getOutputProfiles()) {
- for (const auto& profile : ioProfile->getAudioProfiles()) {
- const auto& channels = profile->getChannels();
- const auto& sampleRates = profile->getSampleRates();
- auto configs = ConfigHelper::combineAudioConfig(
- std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
- std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
- profile->getFormat());
- auto flags = ioProfile->getFlags();
- for (auto& config : configs) {
- // Some combinations of flags declared in the config file require special
- // treatment.
- if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
- config.offloadInfo.sampleRateHz = config.sampleRateHz;
- config.offloadInfo.channelMask = config.channelMask;
- config.offloadInfo.format = config.format;
- config.offloadInfo.streamType = AudioStreamType::MUSIC;
- config.offloadInfo.bitRatePerSecond = 320;
- config.offloadInfo.durationMicroseconds = -1;
- config.offloadInfo.bitWidth = 16;
- config.offloadInfo.bufferSize = 256; // arbitrary value
- config.offloadInfo.usage = AudioUsage::MEDIA;
- result.emplace_back(device, config,
- AudioOutputFlag(AudioOutputFlag::COMPRESS_OFFLOAD |
- AudioOutputFlag::DIRECT));
- } else {
- if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) { // ignore the flag
- flags &= ~AUDIO_OUTPUT_FLAG_PRIMARY;
- }
- result.emplace_back(device, config, AudioOutputFlag(flags));
+static std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(
+ bool oneProfilePerDevice) {
+ std::vector<DeviceConfigParameter> result;
+ for (const auto& device : getDeviceParameters()) {
+ auto module =
+ getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+ for (const auto& ioProfile : module->getOutputProfiles()) {
+ for (const auto& profile : ioProfile->getAudioProfiles()) {
+ const auto& channels = profile->getChannels();
+ const auto& sampleRates = profile->getSampleRates();
+ auto configs = ConfigHelper::combineAudioConfig(
+ std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
+ std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
+ profile->getFormat());
+ auto flags = ioProfile->getFlags();
+ for (auto& config : configs) {
+ // Some combinations of flags declared in the config file require special
+ // treatment.
+ if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ config.offloadInfo.sampleRateHz = config.sampleRateHz;
+ config.offloadInfo.channelMask = config.channelMask;
+ config.offloadInfo.format = config.format;
+ config.offloadInfo.streamType = AudioStreamType::MUSIC;
+ config.offloadInfo.bitRatePerSecond = 320;
+ config.offloadInfo.durationMicroseconds = -1;
+ config.offloadInfo.bitWidth = 16;
+ config.offloadInfo.bufferSize = 256; // arbitrary value
+ config.offloadInfo.usage = AudioUsage::MEDIA;
+ result.emplace_back(device, config,
+ AudioOutputFlag(AudioOutputFlag::COMPRESS_OFFLOAD |
+ AudioOutputFlag::DIRECT));
+ } else {
+ if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) { // ignore the flag
+ flags &= ~AUDIO_OUTPUT_FLAG_PRIMARY;
}
+ result.emplace_back(device, config, AudioOutputFlag(flags));
}
+ if (oneProfilePerDevice) break;
}
+ if (oneProfilePerDevice) break;
}
+ if (oneProfilePerDevice) break;
}
- return result;
- }();
+ }
+ return result;
+}
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
+ static std::vector<DeviceConfigParameter> parameters =
+ generateOutputDeviceConfigParameters(false);
return parameters;
}
-const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
- static std::vector<DeviceConfigParameter> parameters = [] {
- std::vector<DeviceConfigParameter> result;
- for (const auto& device : getDeviceParameters()) {
- auto module =
- getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
- for (const auto& ioProfile : module->getInputProfiles()) {
- for (const auto& profile : ioProfile->getAudioProfiles()) {
- const auto& channels = profile->getChannels();
- const auto& sampleRates = profile->getSampleRates();
- auto configs = ConfigHelper::combineAudioConfig(
- std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
- std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
- profile->getFormat());
- for (const auto& config : configs) {
- result.emplace_back(device, config, AudioInputFlag(ioProfile->getFlags()));
- }
+const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters() {
+ static std::vector<DeviceConfigParameter> parameters =
+ generateOutputDeviceConfigParameters(true);
+ return parameters;
+}
+
+static std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(
+ bool oneProfilePerDevice) {
+ std::vector<DeviceConfigParameter> result;
+ for (const auto& device : getDeviceParameters()) {
+ auto module =
+ getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+ for (const auto& ioProfile : module->getInputProfiles()) {
+ for (const auto& profile : ioProfile->getAudioProfiles()) {
+ const auto& channels = profile->getChannels();
+ const auto& sampleRates = profile->getSampleRates();
+ auto configs = ConfigHelper::combineAudioConfig(
+ std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
+ std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
+ profile->getFormat());
+ for (const auto& config : configs) {
+ result.emplace_back(device, config, AudioInputFlag(ioProfile->getFlags()));
+ if (oneProfilePerDevice) break;
}
+ if (oneProfilePerDevice) break;
}
+ if (oneProfilePerDevice) break;
}
- return result;
- }();
+ }
+ return result;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
+ static std::vector<DeviceConfigParameter> parameters =
+ generateInputDeviceConfigParameters(false);
+ return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters() {
+ static std::vector<DeviceConfigParameter> parameters =
+ generateInputDeviceConfigParameters(true);
return parameters;
}
#endif // MAJOR_VERSION <= 6
-TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedOutputStreams) {
- doc::test("Verify that a device can't be closed if there are streams opened");
+class SingleOutputConfigTest : public AudioHidlTestWithDeviceConfigParameter {};
+TEST_P(SingleOutputConfigTest, CloseDeviceWithOpenedOutputStreams) {
+ doc::test("Verify that a device can't be closed if there are output streams opened");
#if MAJOR_VERSION <= 6
DeviceAddress address{.device = AudioDevice::OUT_DEFAULT};
SourceMetadata initMetadata = {{{AudioUsage::MEDIA, AudioContentType::MUSIC, 1 /* gain */}}};
- auto flags = hidl_bitfield<AudioOutputFlag>(AudioOutputFlag::NONE);
#elif MAJOR_VERSION >= 7
DeviceAddress address{.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT)};
- SourceMetadata initMetadata = {
- {{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
- toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC), 1 /* gain */}}};
- hidl_vec<AudioInOutFlag> flags;
+ SourceMetadata initMetadata = {{{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+ toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+ {} /* tags */,
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
+ 1 /* gain */}}};
#endif
- AudioConfig config{};
+ const AudioConfig& config = getConfig();
+ auto flags = getOutputFlags();
sp<IStreamOut> stream;
StreamHelper<IStreamOut> helper(stream);
AudioConfig suggestedConfig{};
@@ -118,23 +146,27 @@
ASSERT_OK(getDevice()->close());
ASSERT_TRUE(resetDevice());
}
+INSTANTIATE_TEST_CASE_P(SingleOutputConfig, SingleOutputConfigTest,
+ ::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
+ &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleOutputConfig);
-TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedInputStreams) {
- doc::test("Verify that a device can't be closed if there are streams opened");
- if (!getCachedPolicyConfig().haveInputProfilesInModule(getDeviceName())) {
- GTEST_SKIP() << "Device doesn't have input profiles";
- }
+class SingleInputConfigTest : public AudioHidlTestWithDeviceConfigParameter {};
+TEST_P(SingleInputConfigTest, CloseDeviceWithOpenedInputStreams) {
+ doc::test("Verify that a device can't be closed if there are input streams opened");
#if MAJOR_VERSION <= 6
DeviceAddress address{.device = AudioDevice::IN_DEFAULT};
SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}};
- auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
#elif MAJOR_VERSION >= 7
DeviceAddress address{.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT)};
SinkMetadata initMetadata = {
- {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_MIC), .gain = 1}}};
- hidl_vec<AudioInOutFlag> flags;
+ {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_MIC),
+ .gain = 1,
+ .tags = {},
+ .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
#endif
- AudioConfig config{};
+ const AudioConfig& config = getConfig();
+ auto flags = getInputFlags();
sp<IStreamIn> stream;
StreamHelper<IStreamIn> helper(stream);
AudioConfig suggestedConfig{};
@@ -149,6 +181,10 @@
ASSERT_OK(getDevice()->close());
ASSERT_TRUE(resetDevice());
}
+INSTANTIATE_TEST_CASE_P(SingleInputConfig, SingleInputConfigTest,
+ ::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
+ &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleInputConfig);
TEST_P(AudioPatchHidlTest, UpdatePatchInvalidHandle) {
doc::test("Verify that passing an invalid handle to updateAudioPatch is checked");
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 63eaea8..3b6d5f2 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -25,9 +25,7 @@
for (auto channelMask : channelMasks) {
for (auto sampleRate : sampleRates) {
AudioConfig config{};
- // leave offloadInfo to 0
- config.base.channelMask.resize(1);
- config.base.channelMask[0] = toString(channelMask);
+ config.base.channelMask = toString(channelMask);
config.base.sampleRateHz = sampleRate;
config.base.format = format;
configs.push_back(config);
@@ -36,52 +34,158 @@
return configs;
}
+static std::tuple<std::vector<AudioInOutFlag>, bool> generateOutFlags(
+ const xsd::MixPorts::MixPort& mixPort) {
+ static const std::vector<AudioInOutFlag> offloadFlags = {
+ toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
+ toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_DIRECT)};
+ std::vector<AudioInOutFlag> flags;
+ bool isOffload = false;
+ if (mixPort.hasFlags()) {
+ auto xsdFlags = mixPort.getFlags();
+ isOffload = std::find(xsdFlags.begin(), xsdFlags.end(),
+ xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) !=
+ xsdFlags.end();
+ if (!isOffload) {
+ for (auto flag : xsdFlags) {
+ if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) {
+ flags.push_back(toString(flag));
+ }
+ }
+ } else {
+ flags = offloadFlags;
+ }
+ }
+ return {flags, isOffload};
+}
+
+static AudioOffloadInfo generateOffloadInfo(const AudioConfigBase& base) {
+ return AudioOffloadInfo{
+ .base = base,
+ .streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
+ .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+ .bitRatePerSecond = 320,
+ .durationMicroseconds = -1,
+ .bitWidth = 16,
+ .bufferSize = 256 // arbitrary value
+ };
+}
+
+static std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(
+ bool oneProfilePerDevice) {
+ std::vector<DeviceConfigParameter> result;
+ for (const auto& device : getDeviceParameters()) {
+ auto module =
+ getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+ if (!module || !module->getFirstMixPorts()) break;
+ for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+ if (mixPort.getRole() != xsd::Role::source) continue; // not an output profile
+ auto [flags, isOffload] = generateOutFlags(mixPort);
+ for (const auto& profile : mixPort.getProfile()) {
+ auto configs = combineAudioConfig(profile.getChannelMasks(),
+ profile.getSamplingRates(), profile.getFormat());
+ for (auto& config : configs) {
+ // Some combinations of flags declared in the config file require special
+ // treatment.
+ if (isOffload) {
+ config.offloadInfo.info(generateOffloadInfo(config.base));
+ }
+ result.emplace_back(device, config, flags);
+ if (oneProfilePerDevice) break;
+ }
+ if (oneProfilePerDevice) break;
+ }
+ if (oneProfilePerDevice) break;
+ }
+ }
+ return result;
+}
+
const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
- static std::vector<DeviceConfigParameter> parameters = [] {
+ static std::vector<DeviceConfigParameter> parameters =
+ generateOutputDeviceConfigParameters(false);
+ return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters() {
+ static std::vector<DeviceConfigParameter> parameters =
+ generateOutputDeviceConfigParameters(true);
+ return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters(
+ bool generateInvalidFlags = true) {
+ static std::vector<DeviceConfigParameter> parameters = [&] {
std::vector<DeviceConfigParameter> result;
- const std::vector<AudioInOutFlag> offloadFlags = {
- toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
- toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_DIRECT)};
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+ if (!module || !module->getFirstMixPorts()) break;
+ bool hasRegularConfig = false, hasOffloadConfig = false;
for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
if (mixPort.getRole() != xsd::Role::source) continue; // not an output profile
- auto xsdFlags = mixPort.getFlags();
- const bool isOffload =
- std::find(xsdFlags.begin(), xsdFlags.end(),
- xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) !=
- xsdFlags.end();
- std::vector<AudioInOutFlag> flags;
- if (!isOffload) {
- for (auto flag : xsdFlags) {
- if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) {
- flags.push_back(toString(flag));
- }
- }
- } else {
- flags = offloadFlags;
- }
+ auto [validFlags, isOffload] = generateOutFlags(mixPort);
+ if ((!isOffload && hasRegularConfig) || (isOffload && hasOffloadConfig)) continue;
for (const auto& profile : mixPort.getProfile()) {
- auto configs =
- combineAudioConfig(profile.getChannelMasks(),
- profile.getSamplingRates(), profile.getFormat());
- for (auto& config : configs) {
- // Some combinations of flags declared in the config file require special
- // treatment.
+ if (!profile.hasFormat() || !profile.hasSamplingRates() ||
+ !profile.hasChannelMasks())
+ continue;
+ AudioConfigBase validBase = {
+ profile.getFormat(),
+ static_cast<uint32_t>(profile.getSamplingRates()[0]),
+ toString(profile.getChannelMasks()[0])};
+ {
+ AudioConfig config{.base = validBase};
+ config.base.channelMask = "random_string";
if (isOffload) {
- config.offloadInfo.base = config.base;
- config.offloadInfo.streamType =
- toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
- config.offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
- config.offloadInfo.bitRatePerSecond = 320;
- config.offloadInfo.durationMicroseconds = -1;
- config.offloadInfo.bitWidth = 16;
- config.offloadInfo.bufferSize = 256; // arbitrary value
+ config.offloadInfo.info(generateOffloadInfo(validBase));
}
+ result.emplace_back(device, config, validFlags);
+ }
+ {
+ AudioConfig config{.base = validBase};
+ config.base.format = "random_string";
+ if (isOffload) {
+ config.offloadInfo.info(generateOffloadInfo(validBase));
+ }
+ result.emplace_back(device, config, validFlags);
+ }
+ if (generateInvalidFlags) {
+ AudioConfig config{.base = validBase};
+ if (isOffload) {
+ config.offloadInfo.info(generateOffloadInfo(validBase));
+ }
+ std::vector<AudioInOutFlag> flags = {"random_string", ""};
result.emplace_back(device, config, flags);
}
+ if (isOffload) {
+ {
+ AudioConfig config{.base = validBase};
+ config.offloadInfo.info(generateOffloadInfo(validBase));
+ config.offloadInfo.info().base.channelMask = "random_string";
+ }
+ {
+ AudioConfig config{.base = validBase};
+ config.offloadInfo.info(generateOffloadInfo(validBase));
+ config.offloadInfo.info().base.format = "random_string";
+ }
+ {
+ AudioConfig config{.base = validBase};
+ config.offloadInfo.info(generateOffloadInfo(validBase));
+ config.offloadInfo.info().streamType = "random_string";
+ }
+ {
+ AudioConfig config{.base = validBase};
+ config.offloadInfo.info(generateOffloadInfo(validBase));
+ config.offloadInfo.info().usage = "random_string";
+ }
+ hasOffloadConfig = true;
+ } else {
+ hasRegularConfig = true;
+ }
+ break;
}
+ if (hasOffloadConfig && hasRegularConfig) break;
}
}
return result;
@@ -89,28 +193,392 @@
return parameters;
}
+static std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(
+ bool oneProfilePerDevice) {
+ std::vector<DeviceConfigParameter> result;
+ for (const auto& device : getDeviceParameters()) {
+ auto module =
+ getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+ if (!module || !module->getFirstMixPorts()) break;
+ for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+ if (mixPort.getRole() != xsd::Role::sink) continue; // not an input profile
+ std::vector<AudioInOutFlag> flags;
+ if (mixPort.hasFlags()) {
+ std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(),
+ std::back_inserter(flags), [](auto flag) { return toString(flag); });
+ }
+ for (const auto& profile : mixPort.getProfile()) {
+ auto configs = combineAudioConfig(profile.getChannelMasks(),
+ profile.getSamplingRates(), profile.getFormat());
+ for (const auto& config : configs) {
+ result.emplace_back(device, config, flags);
+ if (oneProfilePerDevice) break;
+ }
+ if (oneProfilePerDevice) break;
+ }
+ if (oneProfilePerDevice) break;
+ }
+ }
+ return result;
+}
+
const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
- static std::vector<DeviceConfigParameter> parameters = [] {
+ static std::vector<DeviceConfigParameter> parameters =
+ generateInputDeviceConfigParameters(false);
+ return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters() {
+ static std::vector<DeviceConfigParameter> parameters =
+ generateInputDeviceConfigParameters(true);
+ return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceInvalidConfigParameters(
+ bool generateInvalidFlags = true) {
+ static std::vector<DeviceConfigParameter> parameters = [&] {
std::vector<DeviceConfigParameter> result;
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+ if (!module || !module->getFirstMixPorts()) break;
+ bool hasConfig = false;
for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
if (mixPort.getRole() != xsd::Role::sink) continue; // not an input profile
- std::vector<AudioInOutFlag> flags;
- std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(), flags.begin(),
- [](auto flag) { return toString(flag); });
+ std::vector<AudioInOutFlag> validFlags;
+ if (mixPort.hasFlags()) {
+ std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(),
+ std::back_inserter(validFlags),
+ [](auto flag) { return toString(flag); });
+ }
for (const auto& profile : mixPort.getProfile()) {
- auto configs =
- combineAudioConfig(profile.getChannelMasks(),
- profile.getSamplingRates(), profile.getFormat());
- for (const auto& config : configs) {
+ if (!profile.hasFormat() || !profile.hasSamplingRates() ||
+ !profile.hasChannelMasks())
+ continue;
+ AudioConfigBase validBase = {
+ profile.getFormat(),
+ static_cast<uint32_t>(profile.getSamplingRates()[0]),
+ toString(profile.getChannelMasks()[0])};
+ {
+ AudioConfig config{.base = validBase};
+ config.base.channelMask = "random_string";
+ result.emplace_back(device, config, validFlags);
+ }
+ {
+ AudioConfig config{.base = validBase};
+ config.base.format = "random_string";
+ result.emplace_back(device, config, validFlags);
+ }
+ if (generateInvalidFlags) {
+ AudioConfig config{.base = validBase};
+ std::vector<AudioInOutFlag> flags = {"random_string", ""};
result.emplace_back(device, config, flags);
}
+ hasConfig = true;
+ break;
}
+ if (hasConfig) break;
}
}
return result;
}();
return parameters;
}
+
+class InvalidInputConfigNoFlagsTest : public AudioHidlTestWithDeviceConfigParameter {};
+TEST_P(InvalidInputConfigNoFlagsTest, InputBufferSizeTest) {
+ doc::test("Verify that invalid config is rejected by IDevice::getInputBufferSize method.");
+ uint64_t bufferSize;
+ ASSERT_OK(getDevice()->getInputBufferSize(getConfig(), returnIn(res, bufferSize)));
+ EXPECT_EQ(Result::INVALID_ARGUMENTS, res);
+}
+INSTANTIATE_TEST_CASE_P(
+ InputBufferSizeInvalidConfig, InvalidInputConfigNoFlagsTest,
+ ::testing::ValuesIn(getInputDeviceInvalidConfigParameters(false /*generateInvalidFlags*/)),
+ &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputBufferSizeInvalidConfig);
+
+enum { PARAM_DEVICE_CONFIG, PARAM_ADDRESS, PARAM_METADATA };
+enum { INDEX_SINK, INDEX_SOURCE };
+using SinkOrSourceMetadata = std::variant<SinkMetadata, SourceMetadata>;
+using StreamOpenParameter = std::tuple<DeviceConfigParameter, DeviceAddress, SinkOrSourceMetadata>;
+
+static std::string StreamOpenParameterToString(
+ const ::testing::TestParamInfo<StreamOpenParameter>& info) {
+ return DeviceConfigParameterToString(::testing::TestParamInfo<DeviceConfigParameter>{
+ std::get<PARAM_DEVICE_CONFIG>(info.param), info.index}) +
+ "__" +
+ SanitizeStringForGTestName(
+ ::testing::PrintToString(std::get<PARAM_ADDRESS>(info.param))) +
+ "__" +
+ SanitizeStringForGTestName(std::visit(
+ [](auto&& arg) -> std::string { return ::testing::PrintToString(arg); },
+ std::get<PARAM_METADATA>(info.param)));
+}
+
+class StreamOpenTest : public HidlTest, public ::testing::WithParamInterface<StreamOpenParameter> {
+ protected:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp()); // setup base
+ ASSERT_TRUE(getDevicesFactory() != nullptr);
+ ASSERT_TRUE(getDevice() != nullptr);
+ }
+ const std::string& getFactoryName() const override {
+ return std::get<PARAM_FACTORY_NAME>(
+ std::get<PARAM_DEVICE>(std::get<PARAM_DEVICE_CONFIG>(GetParam())));
+ }
+ const std::string& getDeviceName() const override {
+ return std::get<PARAM_DEVICE_NAME>(
+ std::get<PARAM_DEVICE>(std::get<PARAM_DEVICE_CONFIG>(GetParam())));
+ }
+ const AudioConfig& getConfig() const {
+ return std::get<PARAM_CONFIG>(std::get<PARAM_DEVICE_CONFIG>(GetParam()));
+ }
+ const hidl_vec<AudioInOutFlag> getFlags() const {
+ return std::get<PARAM_FLAGS>(std::get<PARAM_DEVICE_CONFIG>(GetParam()));
+ }
+ const DeviceAddress& getDeviceAddress() const { return std::get<PARAM_ADDRESS>(GetParam()); }
+ bool isParamForInputStream() const {
+ return std::get<PARAM_METADATA>(GetParam()).index() == INDEX_SINK;
+ }
+ const SinkMetadata& getSinkMetadata() const {
+ return std::get<INDEX_SINK>(std::get<PARAM_METADATA>(GetParam()));
+ }
+ const SourceMetadata& getSourceMetadata() const {
+ return std::get<INDEX_SOURCE>(std::get<PARAM_METADATA>(GetParam()));
+ }
+};
+
+static const DeviceAddress& getValidInputDeviceAddress() {
+ static const DeviceAddress valid = {
+ .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT)};
+ return valid;
+}
+
+static const DeviceAddress& getValidOutputDeviceAddress() {
+ static const DeviceAddress valid = {
+ .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT)};
+ return valid;
+}
+
+static const DeviceAddress& getInvalidDeviceAddress() {
+ static const DeviceAddress valid = {.deviceType = "random_string"};
+ return valid;
+}
+
+static const RecordTrackMetadata& getValidRecordTrackMetadata() {
+ static const RecordTrackMetadata valid = {
+ .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT), .gain = 1};
+ return valid;
+}
+
+static const RecordTrackMetadata& getValidRecordTrackMetadataWithDest() {
+ static const RecordTrackMetadata valid = {
+ .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+ .gain = 1,
+ .destination = [] {
+ RecordTrackMetadata::Destination dest;
+ dest.device(getValidOutputDeviceAddress());
+ return dest;
+ }()};
+ return valid;
+}
+
+static const RecordTrackMetadata& getInvalidSourceRecordTrackMetadata() {
+ static const RecordTrackMetadata invalid = {.source = "random_string", .gain = 1};
+ return invalid;
+}
+
+static const RecordTrackMetadata& getRecordTrackMetadataWithInvalidDest() {
+ static const RecordTrackMetadata invalid = {
+ .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+ .gain = 1,
+ .destination = [] {
+ RecordTrackMetadata::Destination dest;
+ dest.device(getInvalidDeviceAddress());
+ return dest;
+ }()};
+ return invalid;
+}
+
+static const RecordTrackMetadata& getInvalidChannelMaskRecordTrackMetadata() {
+ static const RecordTrackMetadata invalid = {
+ .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+ .gain = 1,
+ .channelMask = "random_string"};
+ return invalid;
+}
+
+static const RecordTrackMetadata& getInvalidTagsRecordTrackMetadata() {
+ static const RecordTrackMetadata invalid = {
+ .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+ .gain = 1,
+ .tags = {{"random_string"}}};
+ return invalid;
+}
+
+static const PlaybackTrackMetadata& getValidPlaybackTrackMetadata() {
+ static const PlaybackTrackMetadata valid = {
+ .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+ .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+ .gain = 1};
+ return valid;
+}
+
+static const PlaybackTrackMetadata& getInvalidUsagePlaybackTrackMetadata() {
+ static const PlaybackTrackMetadata invalid = {
+ .usage = "random_string",
+ .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+ .gain = 1};
+ return invalid;
+}
+
+static const PlaybackTrackMetadata& getInvalidContentTypePlaybackTrackMetadata() {
+ static const PlaybackTrackMetadata invalid = {
+ .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+ .contentType = "random_string",
+ .gain = 1};
+ return invalid;
+}
+
+static const PlaybackTrackMetadata& getInvalidChannelMaskPlaybackTrackMetadata() {
+ static const PlaybackTrackMetadata invalid = {
+ .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+ .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+ .gain = 1,
+ .channelMask = "random_string"};
+ return invalid;
+}
+
+static const PlaybackTrackMetadata& getInvalidTagsPlaybackTrackMetadata() {
+ static const PlaybackTrackMetadata invalid = {
+ .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+ .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+ .gain = 1,
+ .tags = {{"random_string"}}};
+ return invalid;
+}
+
+static const std::vector<SourceMetadata>& getInvalidSourceMetadatas() {
+ static const std::vector<SourceMetadata> invalids = {
+ SourceMetadata{.tracks = {{getInvalidUsagePlaybackTrackMetadata()}}},
+ SourceMetadata{.tracks = {{getInvalidContentTypePlaybackTrackMetadata()}}},
+ SourceMetadata{.tracks = {{getInvalidChannelMaskPlaybackTrackMetadata()}}},
+ SourceMetadata{.tracks = {{getInvalidTagsPlaybackTrackMetadata()}}},
+ SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
+ getInvalidUsagePlaybackTrackMetadata()}}},
+ SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
+ getInvalidContentTypePlaybackTrackMetadata()}}},
+ SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
+ getInvalidChannelMaskPlaybackTrackMetadata()}}},
+ SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
+ getInvalidTagsPlaybackTrackMetadata()}}}};
+ return invalids;
+}
+static const std::vector<SinkMetadata>& getInvalidSinkMetadatas() {
+ static const std::vector<SinkMetadata> invalids = {
+ SinkMetadata{.tracks = {{getInvalidSourceRecordTrackMetadata()}}},
+ SinkMetadata{.tracks = {{getRecordTrackMetadataWithInvalidDest()}}},
+ SinkMetadata{.tracks = {{getInvalidChannelMaskRecordTrackMetadata()}}},
+ SinkMetadata{.tracks = {{getInvalidTagsRecordTrackMetadata()}}},
+ SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
+ getInvalidSourceRecordTrackMetadata()}}},
+ SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
+ getRecordTrackMetadataWithInvalidDest()}}},
+ SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
+ getInvalidChannelMaskRecordTrackMetadata()}}},
+ SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
+ getInvalidTagsRecordTrackMetadata()}}}};
+ return invalids;
+}
+template <typename T>
+static inline std::vector<SinkOrSourceMetadata> wrapMetadata(const std::vector<T>& metadata) {
+ return std::vector<SinkOrSourceMetadata>{metadata.begin(), metadata.end()};
+}
+
+TEST_P(StreamOpenTest, OpenInputOrOutputStreamTest) {
+ doc::test(
+ "Verify that invalid arguments are rejected by "
+ "IDevice::open{Input|Output}Stream method.");
+ AudioConfig suggestedConfig{};
+ if (isParamForInputStream()) {
+ sp<IStreamIn> stream;
+ ASSERT_OK(getDevice()->openInputStream(AudioIoHandle{}, getDeviceAddress(), getConfig(),
+ getFlags(), getSinkMetadata(),
+ returnIn(res, stream, suggestedConfig)));
+ ASSERT_TRUE(stream == nullptr);
+ } else {
+ sp<IStreamOut> stream;
+ ASSERT_OK(getDevice()->openOutputStream(AudioIoHandle{}, getDeviceAddress(), getConfig(),
+ getFlags(), getSourceMetadata(),
+ returnIn(res, stream, suggestedConfig)));
+ ASSERT_TRUE(stream == nullptr);
+ }
+ EXPECT_EQ(Result::INVALID_ARGUMENTS, res);
+ EXPECT_EQ(AudioConfig{}, suggestedConfig);
+}
+INSTANTIATE_TEST_CASE_P(
+ InputStreamInvalidConfig, StreamOpenTest,
+ ::testing::Combine(::testing::ValuesIn(getInputDeviceInvalidConfigParameters()),
+ ::testing::Values(getValidInputDeviceAddress()),
+ ::testing::Values(SinkMetadata{
+ .tracks = {{getValidRecordTrackMetadata(),
+ getValidRecordTrackMetadataWithDest()}}})),
+ &StreamOpenParameterToString);
+INSTANTIATE_TEST_CASE_P(
+ InputStreamInvalidAddress, StreamOpenTest,
+ ::testing::Combine(::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
+ ::testing::Values(getInvalidDeviceAddress()),
+ ::testing::Values(SinkMetadata{
+ .tracks = {{getValidRecordTrackMetadata(),
+ getValidRecordTrackMetadataWithDest()}}})),
+ &StreamOpenParameterToString);
+INSTANTIATE_TEST_CASE_P(
+ InputStreamInvalidMetadata, StreamOpenTest,
+ ::testing::Combine(::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
+ ::testing::Values(getValidInputDeviceAddress()),
+ ::testing::ValuesIn(wrapMetadata(getInvalidSinkMetadatas()))),
+ &StreamOpenParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidConfig);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidAddress);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidMetadata);
+
+INSTANTIATE_TEST_CASE_P(
+ OutputStreamInvalidConfig, StreamOpenTest,
+ ::testing::Combine(::testing::ValuesIn(getOutputDeviceInvalidConfigParameters()),
+ ::testing::Values(getValidOutputDeviceAddress()),
+ ::testing::Values(SourceMetadata{
+ .tracks = {{getValidPlaybackTrackMetadata()}}})),
+ &StreamOpenParameterToString);
+INSTANTIATE_TEST_CASE_P(
+ OutputStreamInvalidAddress, StreamOpenTest,
+ ::testing::Combine(::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
+ ::testing::Values(getInvalidDeviceAddress()),
+ ::testing::Values(SourceMetadata{
+ .tracks = {{getValidPlaybackTrackMetadata()}}})),
+ &StreamOpenParameterToString);
+INSTANTIATE_TEST_CASE_P(
+ OutputStreamInvalidMetadata, StreamOpenTest,
+ ::testing::Combine(::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
+ ::testing::Values(getValidOutputDeviceAddress()),
+ ::testing::ValuesIn(wrapMetadata(getInvalidSourceMetadatas()))),
+ &StreamOpenParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidConfig);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidAddress);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidMetadata);
+
+TEST_P(OutputStreamTest, UpdateInvalidSourceMetadata) {
+ doc::test("Verify that invalid metadata is rejected by IStreamOut::updateSourceMetadata");
+ for (const auto& metadata : getInvalidSourceMetadatas()) {
+ ASSERT_RESULT(invalidArgsOrNotSupported, stream->updateSourceMetadata(metadata))
+ << ::testing::PrintToString(metadata);
+ }
+}
+
+TEST_P(InputStreamTest, UpdateInvalidSinkMetadata) {
+ doc::test("Verify that invalid metadata is rejected by IStreamIn::updateSinkMetadata");
+ for (const auto& metadata : getInvalidSinkMetadatas()) {
+ ASSERT_RESULT(invalidArgsOrNotSupported, stream->updateSinkMetadata(metadata))
+ << ::testing::PrintToString(metadata);
+ }
+}
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 d790b34..7d88642 100644
--- a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h
+++ b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h
@@ -33,10 +33,14 @@
if (mConfig) {
mStatus = OK;
mPrimaryModule = getModuleFromName(DeviceManager::kPrimaryDevice);
- for (const auto& module : mConfig->getFirstModules()->get_module()) {
- auto attachedDevices = module.getFirstAttachedDevices()->getItem();
- if (!attachedDevices.empty()) {
- mModulesWithDevicesNames.insert(module.getName());
+ 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());
+ }
+ }
}
}
}
@@ -52,7 +56,7 @@
}
const std::string& getFilePath() const { return mFilePath; }
const xsd::Module* getModuleFromName(const std::string& name) const {
- if (mConfig) {
+ if (mConfig && mConfig->getFirstModules()) {
for (const auto& module : mConfig->getFirstModules()->get_module()) {
if (module.getName() == name) return &module;
}
@@ -65,8 +69,10 @@
}
bool haveInputProfilesInModule(const std::string& name) const {
auto module = getModuleFromName(name);
- for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
- if (mixPort.getRole() == xsd::Role::sink) return true;
+ if (module && module->getFirstMixPorts()) {
+ for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+ if (mixPort.getRole() == xsd::Role::sink) return true;
+ }
}
return false;
}
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index 1ead47c..f145b60 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -156,6 +156,21 @@
return *policyConfig;
}
+TEST(CheckConfig, audioPolicyConfigurationValidation) {
+ const auto factories = ::android::hardware::getAllHalInstanceNames(IDevicesFactory::descriptor);
+ if (factories.size() == 0) {
+ GTEST_SKIP() << "Skipping audioPolicyConfigurationValidation because no factory instances "
+ "are found.";
+ }
+ RecordProperty("description",
+ "Verify that the audio policy configuration file "
+ "is valid according to the schema");
+
+ const char* xsd = "/data/local/tmp/audio_policy_configuration_" STRINGIFY(CPP_VERSION) ".xsd";
+ EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(kConfigFileName,
+ android::audio_get_configuration_paths(), xsd);
+}
+
//////////////////////////////////////////////////////////////////////////////
//////////////////// Test parameter types and definitions ////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -231,21 +246,6 @@
}
};
-TEST(CheckConfig, audioPolicyConfigurationValidation) {
- auto deviceParameters = getDeviceParametersForFactoryTests();
- if (deviceParameters.size() == 0) {
- GTEST_SKIP() << "Skipping audioPolicyConfigurationValidation because no device parameter "
- "is found.";
- }
- RecordProperty("description",
- "Verify that the audio policy configuration file "
- "is valid according to the schema");
-
- const char* xsd = "/data/local/tmp/audio_policy_configuration_" STRINGIFY(CPP_VERSION) ".xsd";
- EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(kConfigFileName,
- android::audio_get_configuration_paths(), xsd);
-}
-
class AudioPolicyConfigTest : public AudioHidlTestWithDeviceParameter {
public:
void SetUp() override {
@@ -522,7 +522,9 @@
#if MAJOR_VERSION >= 6
const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters();
+const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters();
const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters();
+const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters();
#endif
#if MAJOR_VERSION >= 4
@@ -883,7 +885,7 @@
AudioHidlTestWithDeviceConfigParameter::TearDown();
}
- protected:
+ protected:
AudioConfig audioConfig;
DeviceAddress address = {};
sp<Stream> stream;
@@ -926,6 +928,8 @@
const SourceMetadata initMetadata = {
{ { toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+ {},
+ toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
1 /* gain */ } }};
#endif
};
@@ -971,7 +975,7 @@
ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
#if MAJOR_VERSION <= 6
address.device = AudioDevice::IN_DEFAULT;
-#elif MAJOR_VERSION <= 7
+#elif MAJOR_VERSION >= 7
address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
#endif
const AudioConfig& config = getConfig();
@@ -986,12 +990,15 @@
protected:
#if MAJOR_VERSION == 2
- const AudioSource initMetadata = AudioSource::DEFAULT;
+ const AudioSource initMetadata = AudioSource::DEFAULT;
#elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
const SinkMetadata initMetadata = {{ {.source = AudioSource::DEFAULT, .gain = 1 } }};
#elif MAJOR_VERSION >= 7
const SinkMetadata initMetadata = {
- {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT), .gain = 1}}};
+ {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+ .gain = 1,
+ .tags = {},
+ .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
#endif
};
@@ -1179,9 +1186,11 @@
EXPECT_EQ(expectedConfig.channelMask, mask);
EXPECT_EQ(expectedConfig.format, format);
#elif MAJOR_VERSION >= 7
+ Result res;
AudioConfigBase actualConfig{};
- auto ret = stream->getAudioProperties(returnIn(actualConfig));
+ auto ret = stream->getAudioProperties(returnIn(res, actualConfig));
EXPECT_TRUE(ret.isOk());
+ EXPECT_EQ(Result::OK, res);
EXPECT_EQ(expectedConfig.base.sampleRateHz, actualConfig.sampleRateHz);
EXPECT_EQ(expectedConfig.base.channelMask, actualConfig.channelMask);
EXPECT_EQ(expectedConfig.base.format, actualConfig.format);
diff --git a/audio/effect/7.0/IVirtualizerEffect.hal b/audio/effect/7.0/IVirtualizerEffect.hal
index 141b4e6..5d11435 100644
--- a/audio/effect/7.0/IVirtualizerEffect.hal
+++ b/audio/effect/7.0/IVirtualizerEffect.hal
@@ -46,23 +46,38 @@
*/
getStrength() generates (Result retval, uint16_t strength);
- struct SpeakerAngle {
+ struct SpeakerAngles {
/** Speaker channel mask */
- vec<AudioChannelMask> mask;
- // all angles are expressed in degrees and
- // are relative to the listener.
- int16_t azimuth; // 0 is the direction the listener faces
- // 180 is behind the listener
- // -90 is to their left
- int16_t elevation; // 0 is the horizontal plane
- // +90 is above the listener, -90 is below
+ AudioChannelMask mask;
+ /**
+ * Horizontal speaker position angles for each channel ordered from LSb
+ * to MSb in the channel mask. The number of values is the number of
+ * channels in the channel mask.
+ *
+ * All angles are expressed in degrees and are relative to the listener.
+ * - 0 is the direction the listener faces;
+ * - 180 is behind the listener;
+ * - -90 is to their left.
+ */
+ vec<int16_t> azimuth;
+ /**
+ * Vertical speaker position angles for each channel ordered from LSb
+ * to MSb in the channel mask. The number of values is the number of
+ * channels in the channel mask.
+ *
+ * All angles are expressed in degrees and are relative to the listener.
+ * - 0 is the horizontal plane of the listener;
+ * - +90 is above the listener;
+ * - -90 is below the listener.
+ */
+ vec<int16_t> elevation;
};
/**
* Retrieves virtual speaker angles for the given channel mask on the
* specified device.
*/
- getVirtualSpeakerAngles(vec<AudioChannelMask> mask, DeviceAddress device)
- generates (Result retval, vec<SpeakerAngle> speakerAngles);
+ getVirtualSpeakerAngles(AudioChannelMask mask, DeviceAddress device)
+ generates (Result retval, SpeakerAngles speakerAngles);
/**
* Forces the virtualizer effect for the given output device.
diff --git a/audio/effect/7.0/types.hal b/audio/effect/7.0/types.hal
index fe4ee51..c4cb213 100644
--- a/audio/effect/7.0/types.hal
+++ b/audio/effect/7.0/types.hal
@@ -202,16 +202,26 @@
* enumeration of the effect engines present in a library.
*/
struct EffectDescriptor {
- Uuid type; // UUID of to the OpenSL ES interface implemented
- // by this effect
- Uuid uuid; // UUID for this particular implementation
- bitfield<EffectFlags> flags; // effect engine capabilities/requirements flags
- uint16_t cpuLoad; // CPU load indication expressed in 0.1 MIPS units
- // as estimated on an ARM9E core (ARMv5TE) with 0 WS
- uint16_t memoryUsage; // data memory usage expressed in KB and includes
- // only dynamically allocated memory
- uint8_t[64] name; // human readable effect name
- uint8_t[64] implementor; // human readable effect implementor name
+ /** UUID of to the OpenSL ES interface implemented by this effect. */
+ Uuid type;
+ /** UUID for this particular implementation. */
+ Uuid uuid;
+ /** Effect engine capabilities/requirements flags. */
+ bitfield<EffectFlags> flags;
+ /**
+ * CPU load indication expressed in 0.1 MIPS units as estimated on
+ * an ARM9E core (ARMv5TE) with 0 WS.
+ */
+ uint16_t cpuLoad;
+ /**
+ * Data memory usage expressed in KB and includes only dynamically
+ * allocated memory.
+ */
+ uint16_t memoryUsage;
+ /** Human readable effect name. */
+ uint8_t[64] name;
+ /** Human readable effect implementor name. */
+ uint8_t[64] implementor;
};
/**
@@ -242,11 +252,16 @@
*/
@export(name="", value_prefix="EFFECT_CONFIG_")
enum EffectConfigParameters : int32_t {
- BUFFER = 0x0001, // buffer field
- SMP_RATE = 0x0002, // samplingRate
- CHANNELS = 0x0004, // channels
- FORMAT = 0x0008, // format
- ACC_MODE = 0x0010, // accessMode
+ /** Buffer field. */
+ BUFFER = 0x0001,
+ /** Sampling rate. */
+ SMP_RATE = 0x0002,
+ /** Channels. */
+ CHANNELS = 0x0004,
+ /** Format. */
+ FORMAT = 0x0008,
+ /** Access mode. */
+ ACC_MODE = 0x0010,
// Note that the 2.0 ALL have been moved to an helper function
};
@@ -256,9 +271,7 @@
*/
struct EffectBufferConfig {
AudioBuffer buffer;
- uint32_t samplingRateHz;
- AudioChannelMask channels;
- AudioFormat format;
+ AudioConfigBase base;
EffectBufferAccess accessMode;
bitfield<EffectConfigParameters> mask;
};
@@ -270,21 +283,23 @@
@export(name="effect_feature_e", value_prefix="EFFECT_FEATURE_")
enum EffectFeature : int32_t {
- AUX_CHANNELS, // supports auxiliary channels
- // (e.g. dual mic noise suppressor)
+ /** Supports auxiliary channels (e.g. dual mic noise suppressor). */
+ AUX_CHANNELS,
CNT
};
struct EffectAuxChannelsConfig {
- vec<AudioChannelMask> mainChannels; // channel mask for main channels
- vec<AudioChannelMask> auxChannels; // channel mask for auxiliary channels
+ /** Channel mask for main channels. */
+ AudioChannelMask mainChannels;
+ /** Channel mask for auxiliary channels. */
+ AudioChannelMask auxChannels;
};
struct EffectOffloadParameter {
- bool isOffload; // true if the playback thread the effect
- // is attached to is offloaded
- AudioIoHandle ioHandle; // io handle of the playback thread
- // the effect is attached to
+ /** True if the playback thread the effect is attached to is offloaded. */
+ bool isOffload;
+ /** I/O handle of the playback thread the effect is attached to. */
+ AudioIoHandle ioHandle;
};
/**
diff --git a/audio/effect/all-versions/default/AcousticEchoCancelerEffect.cpp b/audio/effect/all-versions/default/AcousticEchoCancelerEffect.cpp
index 137ea24..c1a8b55 100644
--- a/audio/effect/all-versions/default/AcousticEchoCancelerEffect.cpp
+++ b/audio/effect/all-versions/default/AcousticEchoCancelerEffect.cpp
@@ -31,9 +31,7 @@
namespace implementation {
AcousticEchoCancelerEffect::AcousticEchoCancelerEffect(effect_handle_t handle)
- : mEffect(new Effect(handle)) {}
-
-AcousticEchoCancelerEffect::~AcousticEchoCancelerEffect() {}
+ : mEffect(new Effect(true /*isInput*/, handle)) {}
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
Return<Result> AcousticEchoCancelerEffect::init() {
@@ -58,10 +56,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> AcousticEchoCancelerEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> AcousticEchoCancelerEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> AcousticEchoCancelerEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> AcousticEchoCancelerEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> AcousticEchoCancelerEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> AcousticEchoCancelerEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> AcousticEchoCancelerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -82,10 +102,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> AcousticEchoCancelerEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> AcousticEchoCancelerEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -108,10 +124,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> AcousticEchoCancelerEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> AcousticEchoCancelerEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/AcousticEchoCancelerEffect.h b/audio/effect/all-versions/default/AcousticEchoCancelerEffect.h
index 971f64d..d7a84f2 100644
--- a/audio/effect/all-versions/default/AcousticEchoCancelerEffect.h
+++ b/audio/effect/all-versions/default/AcousticEchoCancelerEffect.h
@@ -53,7 +53,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -98,7 +104,7 @@
private:
sp<Effect> mEffect;
- virtual ~AcousticEchoCancelerEffect();
+ virtual ~AcousticEchoCancelerEffect() = default;
};
} // namespace implementation
diff --git a/audio/effect/all-versions/default/Android.bp b/audio/effect/all-versions/default/Android.bp
index 1c3dc74..a0cd612 100644
--- a/audio/effect/all-versions/default/Android.bp
+++ b/audio/effect/all-versions/default/Android.bp
@@ -105,7 +105,6 @@
}
cc_library_shared {
- enabled: false,
name: "android.hardware.audio.effect@7.0-impl",
defaults: ["android.hardware.audio.effect-impl_default"],
shared_libs: [
diff --git a/audio/effect/all-versions/default/AutomaticGainControlEffect.cpp b/audio/effect/all-versions/default/AutomaticGainControlEffect.cpp
index 655a4cd..110b1b6 100644
--- a/audio/effect/all-versions/default/AutomaticGainControlEffect.cpp
+++ b/audio/effect/all-versions/default/AutomaticGainControlEffect.cpp
@@ -30,9 +30,7 @@
namespace implementation {
AutomaticGainControlEffect::AutomaticGainControlEffect(effect_handle_t handle)
- : mEffect(new Effect(handle)) {}
-
-AutomaticGainControlEffect::~AutomaticGainControlEffect() {}
+ : mEffect(new Effect(true /*isInput*/, handle)) {}
void AutomaticGainControlEffect::propertiesFromHal(
const t_agc_settings& halProperties, IAutomaticGainControlEffect::AllProperties* properties) {
@@ -71,10 +69,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> AutomaticGainControlEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> AutomaticGainControlEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> AutomaticGainControlEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> AutomaticGainControlEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> AutomaticGainControlEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> AutomaticGainControlEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> AutomaticGainControlEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -95,10 +115,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> AutomaticGainControlEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> AutomaticGainControlEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -121,10 +137,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> AutomaticGainControlEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> AutomaticGainControlEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/AutomaticGainControlEffect.h b/audio/effect/all-versions/default/AutomaticGainControlEffect.h
index 67e260a..f30d7a5 100644
--- a/audio/effect/all-versions/default/AutomaticGainControlEffect.h
+++ b/audio/effect/all-versions/default/AutomaticGainControlEffect.h
@@ -55,7 +55,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -63,14 +71,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -107,7 +113,7 @@
private:
sp<Effect> mEffect;
- virtual ~AutomaticGainControlEffect();
+ virtual ~AutomaticGainControlEffect() = default;
void propertiesFromHal(const t_agc_settings& halProperties,
IAutomaticGainControlEffect::AllProperties* properties);
diff --git a/audio/effect/all-versions/default/BassBoostEffect.cpp b/audio/effect/all-versions/default/BassBoostEffect.cpp
index 04fd486..33fea3b 100644
--- a/audio/effect/all-versions/default/BassBoostEffect.cpp
+++ b/audio/effect/all-versions/default/BassBoostEffect.cpp
@@ -30,9 +30,8 @@
namespace CPP_VERSION {
namespace implementation {
-BassBoostEffect::BassBoostEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
-
-BassBoostEffect::~BassBoostEffect() {}
+BassBoostEffect::BassBoostEffect(effect_handle_t handle)
+ : mEffect(new Effect(false /*isInput*/, handle)) {}
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
Return<Result> BassBoostEffect::init() {
@@ -57,10 +56,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> BassBoostEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> BassBoostEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> BassBoostEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> BassBoostEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> BassBoostEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> BassBoostEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> BassBoostEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -80,10 +101,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> BassBoostEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> BassBoostEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -105,10 +122,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> BassBoostEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> BassBoostEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/BassBoostEffect.h b/audio/effect/all-versions/default/BassBoostEffect.h
index b89bb22..48f586c 100644
--- a/audio/effect/all-versions/default/BassBoostEffect.h
+++ b/audio/effect/all-versions/default/BassBoostEffect.h
@@ -55,7 +55,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -63,14 +71,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -100,7 +106,7 @@
private:
sp<Effect> mEffect;
- virtual ~BassBoostEffect();
+ virtual ~BassBoostEffect() = default;
};
} // namespace implementation
diff --git a/audio/effect/all-versions/default/Conversions.cpp b/audio/effect/all-versions/default/Conversions.cpp
index b1c0b0d..0cc8767 100644
--- a/audio/effect/all-versions/default/Conversions.cpp
+++ b/audio/effect/all-versions/default/Conversions.cpp
@@ -15,7 +15,7 @@
*/
#include "Conversions.h"
-#include "HidlUtils.h"
+#include "UuidUtils.h"
#include <memory.h>
#include <stdio.h>
@@ -31,12 +31,12 @@
namespace CPP_VERSION {
namespace implementation {
-using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
void effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
EffectDescriptor* descriptor) {
- HidlUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
- HidlUtils::uuidFromHal(halDescriptor.uuid, &descriptor->uuid);
+ UuidUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
+ UuidUtils::uuidFromHal(halDescriptor.uuid, &descriptor->uuid);
descriptor->flags = EnumBitfield<EffectFlags>(halDescriptor.flags);
descriptor->cpuLoad = halDescriptor.cpuLoad;
descriptor->memoryUsage = halDescriptor.memoryUsage;
diff --git a/audio/effect/all-versions/default/DownmixEffect.cpp b/audio/effect/all-versions/default/DownmixEffect.cpp
index c001a5f..f324cff 100644
--- a/audio/effect/all-versions/default/DownmixEffect.cpp
+++ b/audio/effect/all-versions/default/DownmixEffect.cpp
@@ -30,9 +30,8 @@
namespace CPP_VERSION {
namespace implementation {
-DownmixEffect::DownmixEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
-
-DownmixEffect::~DownmixEffect() {}
+DownmixEffect::DownmixEffect(effect_handle_t handle)
+ : mEffect(new Effect(false /*isInput*/, handle)) {}
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
Return<Result> DownmixEffect::init() {
@@ -57,10 +56,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> DownmixEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> DownmixEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> DownmixEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> DownmixEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> DownmixEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> DownmixEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> DownmixEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -80,10 +101,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> DownmixEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> DownmixEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -105,10 +122,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> DownmixEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> DownmixEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/DownmixEffect.h b/audio/effect/all-versions/default/DownmixEffect.h
index 40e462e..caacd06 100644
--- a/audio/effect/all-versions/default/DownmixEffect.h
+++ b/audio/effect/all-versions/default/DownmixEffect.h
@@ -53,7 +53,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -97,7 +103,7 @@
private:
sp<Effect> mEffect;
- virtual ~DownmixEffect();
+ virtual ~DownmixEffect() = default;
};
} // namespace implementation
diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp
index 406a571..edd364c 100644
--- a/audio/effect/all-versions/default/Effect.cpp
+++ b/audio/effect/all-versions/default/Effect.cpp
@@ -27,6 +27,7 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <HidlUtils.h>
#include <android/log.h>
#include <media/EffectsFactoryApi.h>
#include <utils/Trace.h>
@@ -40,7 +41,10 @@
namespace CPP_VERSION {
namespace implementation {
+#if MAJOR_VERSION <= 6
using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioChannelBitfield;
+#endif
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
namespace {
@@ -136,9 +140,12 @@
const char* Effect::sContextResultOfCommand = "returned status";
const char* Effect::sContextCallToCommand = "error";
const char* Effect::sContextCallFunction = sContextCallToCommand;
+const char* Effect::sContextConversion = "conversion";
-Effect::Effect(effect_handle_t handle)
- : mHandle(handle), mEfGroup(nullptr), mStopProcessThread(false) {}
+Effect::Effect(bool isInput, effect_handle_t handle)
+ : mIsInput(isInput), mHandle(handle), mEfGroup(nullptr), mStopProcessThread(false) {
+ (void)mIsInput; // prevent 'unused field' warnings in pre-V7 versions.
+}
Effect::~Effect() {
ATRACE_CALL();
@@ -180,7 +187,8 @@
return halData;
}
-// static
+#if MAJOR_VERSION <= 6
+
void Effect::effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
EffectAuxChannelsConfig* config) {
config->mainChannels = AudioChannelBitfield(halConfig.main_channels);
@@ -194,7 +202,6 @@
halConfig->aux_channels = static_cast<audio_channel_mask_t>(config.auxChannels);
}
-// static
void Effect::effectBufferConfigFromHal(const buffer_config_t& halConfig,
EffectBufferConfig* config) {
config->buffer.id = 0;
@@ -223,7 +230,56 @@
halConfig->mask = static_cast<uint8_t>(config.mask);
}
+#else // MAJOR_VERSION <= 6
+
+void Effect::effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
+ EffectAuxChannelsConfig* config) {
+ (void)HidlUtils::audioChannelMaskFromHal(halConfig.main_channels, mIsInput,
+ &config->mainChannels);
+ (void)HidlUtils::audioChannelMaskFromHal(halConfig.aux_channels, mIsInput,
+ &config->auxChannels);
+}
+
// static
+void Effect::effectAuxChannelsConfigToHal(const EffectAuxChannelsConfig& config,
+ channel_config_t* halConfig) {
+ (void)HidlUtils::audioChannelMaskToHal(config.mainChannels, &halConfig->main_channels);
+ (void)HidlUtils::audioChannelMaskToHal(config.auxChannels, &halConfig->aux_channels);
+}
+
+void Effect::effectBufferConfigFromHal(const buffer_config_t& halConfig,
+ EffectBufferConfig* config) {
+ config->buffer.id = 0;
+ config->buffer.frameCount = 0;
+ audio_config_base_t halConfigBase = {halConfig.samplingRate,
+ static_cast<audio_channel_mask_t>(halConfig.channels),
+ static_cast<audio_format_t>(halConfig.format)};
+ (void)HidlUtils::audioConfigBaseFromHal(halConfigBase, mIsInput, &config->base);
+ config->accessMode = EffectBufferAccess(halConfig.accessMode);
+ config->mask = static_cast<decltype(config->mask)>(halConfig.mask);
+}
+
+// static
+void Effect::effectBufferConfigToHal(const EffectBufferConfig& config, buffer_config_t* halConfig) {
+ // Note: setting the buffers directly is considered obsolete. They need to be set
+ // using 'setProcessBuffers'.
+ halConfig->buffer.frameCount = 0;
+ halConfig->buffer.raw = nullptr;
+ audio_config_base_t halConfigBase;
+ (void)HidlUtils::audioConfigBaseToHal(config.base, &halConfigBase);
+ halConfig->samplingRate = halConfigBase.sample_rate;
+ halConfig->channels = halConfigBase.channel_mask;
+ halConfig->format = halConfigBase.format;
+ // Note: The framework code does not use BP.
+ halConfig->bufferProvider.cookie = nullptr;
+ halConfig->bufferProvider.getBuffer = nullptr;
+ halConfig->bufferProvider.releaseBuffer = nullptr;
+ halConfig->accessMode = static_cast<uint8_t>(config.accessMode);
+ halConfig->mask = static_cast<uint8_t>(config.mask);
+}
+
+#endif // MAJOR_VERSION <= 6
+
void Effect::effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config) {
effectBufferConfigFromHal(halConfig.inputCfg, &config->inputCfg);
effectBufferConfigFromHal(halConfig.outputCfg, &config->outputCfg);
@@ -507,11 +563,65 @@
return sendCommandReturningStatus(EFFECT_CMD_DISABLE, "DISABLE");
}
+Return<Result> Effect::setAudioSource(
+#if MAJOR_VERSION <= 6
+ AudioSource source
+#else
+ const AudioSource& source
+#endif
+) {
+ audio_source_t halSource;
+ if (status_t status = HidlUtils::audioSourceToHal(source, &halSource); status == NO_ERROR) {
+ uint32_t halSourceParam = static_cast<uint32_t>(halSource);
+ return sendCommand(EFFECT_CMD_SET_AUDIO_SOURCE, "SET_AUDIO_SOURCE", sizeof(uint32_t),
+ &halSourceParam);
+ } else {
+ return analyzeStatus(__func__, "audioSourceToHal", sContextConversion, status);
+ }
+}
+
+#if MAJOR_VERSION <= 6
+
Return<Result> Effect::setDevice(AudioDeviceBitfield device) {
uint32_t halDevice = static_cast<uint32_t>(device);
return sendCommand(EFFECT_CMD_SET_DEVICE, "SET_DEVICE", sizeof(uint32_t), &halDevice);
}
+Return<Result> Effect::setInputDevice(AudioDeviceBitfield device) {
+ uint32_t halDevice = static_cast<uint32_t>(device);
+ return sendCommand(EFFECT_CMD_SET_INPUT_DEVICE, "SET_INPUT_DEVICE", sizeof(uint32_t),
+ &halDevice);
+}
+
+#else // MAJOR_VERSION <= 6
+
+Return<Result> Effect::setDevice(const DeviceAddress& device) {
+ audio_devices_t halDevice;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+ if (status_t status = HidlUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress);
+ status == NO_ERROR) {
+ uint32_t halDeviceParam = static_cast<uint32_t>(halDevice);
+ return sendCommand(EFFECT_CMD_SET_DEVICE, "SET_DEVICE", sizeof(uint32_t), &halDeviceParam);
+ } else {
+ return analyzeStatus(__func__, "deviceAddressToHal", sContextConversion, status);
+ }
+}
+
+Return<Result> Effect::setInputDevice(const DeviceAddress& device) {
+ audio_devices_t halDevice;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+ if (status_t status = HidlUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress);
+ status == NO_ERROR) {
+ uint32_t halDeviceParam = static_cast<uint32_t>(halDevice);
+ return sendCommand(EFFECT_CMD_SET_INPUT_DEVICE, "SET_INPUT_DEVICE", sizeof(uint32_t),
+ &halDeviceParam);
+ } else {
+ return analyzeStatus(__func__, "deviceAddressToHal", sContextConversion, status);
+ }
+}
+
+#endif // MAJOR_VERSION <= 6
+
Return<void> Effect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
uint32_t halDataSize;
@@ -546,12 +656,6 @@
inputBufferProvider, outputBufferProvider);
}
-Return<Result> Effect::setInputDevice(AudioDeviceBitfield device) {
- uint32_t halDevice = static_cast<uint32_t>(device);
- return sendCommand(EFFECT_CMD_SET_INPUT_DEVICE, "SET_INPUT_DEVICE", sizeof(uint32_t),
- &halDevice);
-}
-
Return<void> Effect::getConfig(getConfig_cb _hidl_cb) {
getConfigImpl(EFFECT_CMD_GET_CONFIG, "GET_CONFIG", _hidl_cb);
return Void();
@@ -598,12 +702,6 @@
"SET_FEATURE_CONFIG AUX_CHANNELS", halCmd.size(), &halCmd[0]);
}
-Return<Result> Effect::setAudioSource(AudioSource source) {
- uint32_t halSource = static_cast<uint32_t>(source);
- return sendCommand(EFFECT_CMD_SET_AUDIO_SOURCE, "SET_AUDIO_SOURCE", sizeof(uint32_t),
- &halSource);
-}
-
Return<Result> Effect::offload(const EffectOffloadParameter& param) {
effect_offload_param_t halParam;
effectOffloadParamToHal(param, &halParam);
diff --git a/audio/effect/all-versions/default/Effect.h b/audio/effect/all-versions/default/Effect.h
index 181e542..9aa47ea 100644
--- a/audio/effect/all-versions/default/Effect.h
+++ b/audio/effect/all-versions/default/Effect.h
@@ -47,7 +47,9 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
+#if MAJOR_VERSION <= 6
using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioDeviceBitfield;
+#endif
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::effect::CPP_VERSION;
@@ -56,7 +58,7 @@
using GetParameterSuccessCallback =
std::function<void(uint32_t valueSize, const void* valueData)>;
- explicit Effect(effect_handle_t handle);
+ Effect(bool isInput, effect_handle_t handle);
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
Return<Result> init() override;
@@ -66,7 +68,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -74,14 +84,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -104,6 +112,10 @@
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
// Utility methods for extending interfaces.
+ static const char* sContextConversion;
+
+ Result analyzeStatus(const char* funcName, const char* subFuncName,
+ const char* contextDescription, status_t status);
template <typename T>
Return<void> getIntegerParam(uint32_t paramId,
std::function<void(Result retval, T paramValue)> cb) {
@@ -170,6 +182,7 @@
static const char* sContextCallToCommand;
static const char* sContextCallFunction;
+ const bool mIsInput;
effect_handle_t mHandle;
sp<AudioBufferWrapper> mInBuffer;
sp<AudioBufferWrapper> mOutBuffer;
@@ -186,15 +199,14 @@
static size_t alignedSizeIn(size_t s);
template <typename T>
std::unique_ptr<uint8_t[]> hidlVecToHal(const hidl_vec<T>& vec, uint32_t* halDataSize);
- static void effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
- EffectAuxChannelsConfig* config);
+ void effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
+ EffectAuxChannelsConfig* config);
static void effectAuxChannelsConfigToHal(const EffectAuxChannelsConfig& config,
channel_config_t* halConfig);
- static void effectBufferConfigFromHal(const buffer_config_t& halConfig,
- EffectBufferConfig* config);
+ void effectBufferConfigFromHal(const buffer_config_t& halConfig, EffectBufferConfig* config);
static void effectBufferConfigToHal(const EffectBufferConfig& config,
buffer_config_t* halConfig);
- static void effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config);
+ void effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config);
static void effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig);
static void effectOffloadParamToHal(const EffectOffloadParameter& offload,
effect_offload_param_t* halOffload);
@@ -202,8 +214,6 @@
uint32_t valueSize, const void** valueData);
Result analyzeCommandStatus(const char* commandName, const char* context, status_t status);
- Result analyzeStatus(const char* funcName, const char* subFuncName,
- const char* contextDescription, status_t status);
void getConfigImpl(int commandCode, const char* commandName, GetConfigCallback cb);
Result getCurrentConfigImpl(uint32_t featureId, uint32_t configSize,
GetCurrentConfigSuccessCallback onSuccess);
diff --git a/audio/effect/all-versions/default/EffectsFactory.cpp b/audio/effect/all-versions/default/EffectsFactory.cpp
index acce7de..1ea990b 100644
--- a/audio/effect/all-versions/default/EffectsFactory.cpp
+++ b/audio/effect/all-versions/default/EffectsFactory.cpp
@@ -24,10 +24,10 @@
#include "Effect.h"
#include "EnvironmentalReverbEffect.h"
#include "EqualizerEffect.h"
-#include "HidlUtils.h"
#include "LoudnessEnhancerEffect.h"
#include "NoiseSuppressionEffect.h"
#include "PresetReverbEffect.h"
+#include "UuidUtils.h"
#include "VirtualizerEffect.h"
#include "VisualizerEffect.h"
#include "common/all-versions/default/EffectMap.h"
@@ -53,7 +53,7 @@
namespace CPP_VERSION {
namespace implementation {
-using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
// static
sp<IEffect> EffectsFactory::dispatchEffectInstanceCreation(const effect_descriptor_t& halDescriptor,
@@ -82,7 +82,9 @@
} else if (memcmp(halUuid, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) {
return new VisualizerEffect(handle);
}
- return new Effect(handle);
+ const bool isInput =
+ (halDescriptor.flags & EFFECT_FLAG_TYPE_PRE_PROC) == EFFECT_FLAG_TYPE_PRE_PROC;
+ return new Effect(isInput, handle);
}
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffectsFactory follow.
@@ -135,7 +137,7 @@
Return<void> EffectsFactory::getDescriptor(const Uuid& uuid, getDescriptor_cb _hidl_cb) {
effect_uuid_t halUuid;
- HidlUtils::uuidToHal(uuid, &halUuid);
+ UuidUtils::uuidToHal(uuid, &halUuid);
effect_descriptor_t halDescriptor;
status_t status = EffectGetDescriptor(&halUuid, &halDescriptor);
EffectDescriptor descriptor;
@@ -170,7 +172,7 @@
Return<void> EffectsFactory::createEffectImpl(const Uuid& uuid, int32_t session, int32_t ioHandle,
int32_t device, createEffect_cb _hidl_cb) {
effect_uuid_t halUuid;
- HidlUtils::uuidToHal(uuid, &halUuid);
+ UuidUtils::uuidToHal(uuid, &halUuid);
effect_handle_t handle;
Result retval(Result::OK);
status_t status;
diff --git a/audio/effect/all-versions/default/EnvironmentalReverbEffect.cpp b/audio/effect/all-versions/default/EnvironmentalReverbEffect.cpp
index 78122d4..e95a267 100644
--- a/audio/effect/all-versions/default/EnvironmentalReverbEffect.cpp
+++ b/audio/effect/all-versions/default/EnvironmentalReverbEffect.cpp
@@ -31,9 +31,7 @@
namespace implementation {
EnvironmentalReverbEffect::EnvironmentalReverbEffect(effect_handle_t handle)
- : mEffect(new Effect(handle)) {}
-
-EnvironmentalReverbEffect::~EnvironmentalReverbEffect() {}
+ : mEffect(new Effect(false /*isInput*/, handle)) {}
void EnvironmentalReverbEffect::propertiesFromHal(
const t_reverb_settings& halProperties, IEnvironmentalReverbEffect::AllProperties* properties) {
@@ -86,10 +84,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> EnvironmentalReverbEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> EnvironmentalReverbEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> EnvironmentalReverbEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> EnvironmentalReverbEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> EnvironmentalReverbEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> EnvironmentalReverbEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> EnvironmentalReverbEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -110,10 +130,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> EnvironmentalReverbEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> EnvironmentalReverbEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -136,10 +152,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> EnvironmentalReverbEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> EnvironmentalReverbEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/EnvironmentalReverbEffect.h b/audio/effect/all-versions/default/EnvironmentalReverbEffect.h
index bb422d4..9694b5d 100644
--- a/audio/effect/all-versions/default/EnvironmentalReverbEffect.h
+++ b/audio/effect/all-versions/default/EnvironmentalReverbEffect.h
@@ -57,7 +57,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -65,14 +73,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -125,7 +131,7 @@
private:
sp<Effect> mEffect;
- virtual ~EnvironmentalReverbEffect();
+ virtual ~EnvironmentalReverbEffect() = default;
void propertiesFromHal(const t_reverb_settings& halProperties,
IEnvironmentalReverbEffect::AllProperties* properties);
diff --git a/audio/effect/all-versions/default/EqualizerEffect.cpp b/audio/effect/all-versions/default/EqualizerEffect.cpp
index 1b983ec..fffe8cd 100644
--- a/audio/effect/all-versions/default/EqualizerEffect.cpp
+++ b/audio/effect/all-versions/default/EqualizerEffect.cpp
@@ -31,9 +31,8 @@
namespace CPP_VERSION {
namespace implementation {
-EqualizerEffect::EqualizerEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
-
-EqualizerEffect::~EqualizerEffect() {}
+EqualizerEffect::EqualizerEffect(effect_handle_t handle)
+ : mEffect(new Effect(false /*isInput*/, handle)) {}
void EqualizerEffect::propertiesFromHal(const t_equalizer_settings& halProperties,
IEqualizerEffect::AllProperties* properties) {
@@ -80,10 +79,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> EqualizerEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> EqualizerEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> EqualizerEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> EqualizerEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> EqualizerEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> EqualizerEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> EqualizerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -103,10 +124,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> EqualizerEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> EqualizerEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -128,10 +145,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> EqualizerEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> EqualizerEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/EqualizerEffect.h b/audio/effect/all-versions/default/EqualizerEffect.h
index b1cbefd..7a6bc0a 100644
--- a/audio/effect/all-versions/default/EqualizerEffect.h
+++ b/audio/effect/all-versions/default/EqualizerEffect.h
@@ -57,7 +57,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -65,14 +73,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -111,7 +117,7 @@
private:
sp<Effect> mEffect;
- virtual ~EqualizerEffect();
+ virtual ~EqualizerEffect() = default;
void propertiesFromHal(const t_equalizer_settings& halProperties,
IEqualizerEffect::AllProperties* properties);
diff --git a/audio/effect/all-versions/default/LoudnessEnhancerEffect.cpp b/audio/effect/all-versions/default/LoudnessEnhancerEffect.cpp
index ebd5197..c7add86 100644
--- a/audio/effect/all-versions/default/LoudnessEnhancerEffect.cpp
+++ b/audio/effect/all-versions/default/LoudnessEnhancerEffect.cpp
@@ -33,9 +33,7 @@
namespace implementation {
LoudnessEnhancerEffect::LoudnessEnhancerEffect(effect_handle_t handle)
- : mEffect(new Effect(handle)) {}
-
-LoudnessEnhancerEffect::~LoudnessEnhancerEffect() {}
+ : mEffect(new Effect(false /*isInput*/, handle)) {}
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
Return<Result> LoudnessEnhancerEffect::init() {
@@ -60,10 +58,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> LoudnessEnhancerEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> LoudnessEnhancerEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> LoudnessEnhancerEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> LoudnessEnhancerEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> LoudnessEnhancerEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> LoudnessEnhancerEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> LoudnessEnhancerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -83,10 +103,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> LoudnessEnhancerEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> LoudnessEnhancerEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -108,10 +124,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> LoudnessEnhancerEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> LoudnessEnhancerEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/LoudnessEnhancerEffect.h b/audio/effect/all-versions/default/LoudnessEnhancerEffect.h
index 8baf128..6d80207 100644
--- a/audio/effect/all-versions/default/LoudnessEnhancerEffect.h
+++ b/audio/effect/all-versions/default/LoudnessEnhancerEffect.h
@@ -53,7 +53,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -98,7 +104,7 @@
private:
sp<Effect> mEffect;
- virtual ~LoudnessEnhancerEffect();
+ virtual ~LoudnessEnhancerEffect() = default;
};
} // namespace implementation
diff --git a/audio/effect/all-versions/default/NoiseSuppressionEffect.cpp b/audio/effect/all-versions/default/NoiseSuppressionEffect.cpp
index d01bbe5..9e75237 100644
--- a/audio/effect/all-versions/default/NoiseSuppressionEffect.cpp
+++ b/audio/effect/all-versions/default/NoiseSuppressionEffect.cpp
@@ -30,9 +30,7 @@
namespace implementation {
NoiseSuppressionEffect::NoiseSuppressionEffect(effect_handle_t handle)
- : mEffect(new Effect(handle)) {}
-
-NoiseSuppressionEffect::~NoiseSuppressionEffect() {}
+ : mEffect(new Effect(true /*isInput*/, handle)) {}
void NoiseSuppressionEffect::propertiesFromHal(const t_ns_settings& halProperties,
INoiseSuppressionEffect::AllProperties* properties) {
@@ -69,10 +67,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> NoiseSuppressionEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> NoiseSuppressionEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> NoiseSuppressionEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> NoiseSuppressionEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> NoiseSuppressionEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> NoiseSuppressionEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> NoiseSuppressionEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -92,10 +112,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> NoiseSuppressionEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> NoiseSuppressionEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -117,10 +133,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> NoiseSuppressionEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> NoiseSuppressionEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/NoiseSuppressionEffect.h b/audio/effect/all-versions/default/NoiseSuppressionEffect.h
index c49bf7b..6cc45b9 100644
--- a/audio/effect/all-versions/default/NoiseSuppressionEffect.h
+++ b/audio/effect/all-versions/default/NoiseSuppressionEffect.h
@@ -55,7 +55,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -63,14 +71,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -105,7 +111,7 @@
private:
sp<Effect> mEffect;
- virtual ~NoiseSuppressionEffect();
+ virtual ~NoiseSuppressionEffect() = default;
void propertiesFromHal(const t_ns_settings& halProperties,
INoiseSuppressionEffect::AllProperties* properties);
diff --git a/audio/effect/all-versions/default/PresetReverbEffect.cpp b/audio/effect/all-versions/default/PresetReverbEffect.cpp
index 4a2a3a4..1ae8492 100644
--- a/audio/effect/all-versions/default/PresetReverbEffect.cpp
+++ b/audio/effect/all-versions/default/PresetReverbEffect.cpp
@@ -30,9 +30,8 @@
namespace CPP_VERSION {
namespace implementation {
-PresetReverbEffect::PresetReverbEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
-
-PresetReverbEffect::~PresetReverbEffect() {}
+PresetReverbEffect::PresetReverbEffect(effect_handle_t handle)
+ : mEffect(new Effect(false /*isInput*/, handle)) {}
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
Return<Result> PresetReverbEffect::init() {
@@ -57,10 +56,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> PresetReverbEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> PresetReverbEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> PresetReverbEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> PresetReverbEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> PresetReverbEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> PresetReverbEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> PresetReverbEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -80,10 +101,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> PresetReverbEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> PresetReverbEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -105,10 +122,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> PresetReverbEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> PresetReverbEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/PresetReverbEffect.h b/audio/effect/all-versions/default/PresetReverbEffect.h
index 58a6829..eb55e20 100644
--- a/audio/effect/all-versions/default/PresetReverbEffect.h
+++ b/audio/effect/all-versions/default/PresetReverbEffect.h
@@ -53,7 +53,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -98,7 +104,7 @@
private:
sp<Effect> mEffect;
- virtual ~PresetReverbEffect();
+ virtual ~PresetReverbEffect() = default;
};
} // namespace implementation
diff --git a/audio/effect/all-versions/default/VirtualizerEffect.cpp b/audio/effect/all-versions/default/VirtualizerEffect.cpp
index 1b69a90..1dce181 100644
--- a/audio/effect/all-versions/default/VirtualizerEffect.cpp
+++ b/audio/effect/all-versions/default/VirtualizerEffect.cpp
@@ -19,7 +19,9 @@
#include "VirtualizerEffect.h"
#include <memory.h>
+#include <stdlib.h>
+#include <HidlUtils.h>
#include <android/log.h>
#include <system/audio_effects/effect_virtualizer.h>
@@ -32,19 +34,10 @@
namespace CPP_VERSION {
namespace implementation {
-VirtualizerEffect::VirtualizerEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
-VirtualizerEffect::~VirtualizerEffect() {}
-
-void VirtualizerEffect::speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
- hidl_vec<SpeakerAngle>& speakerAngles) {
- speakerAngles.resize(channelCount);
- for (uint32_t i = 0; i < channelCount; ++i) {
- speakerAngles[i].mask = AudioChannelBitfield(*halAngles++);
- speakerAngles[i].azimuth = *halAngles++;
- speakerAngles[i].elevation = *halAngles++;
- }
-}
+VirtualizerEffect::VirtualizerEffect(effect_handle_t handle)
+ : mEffect(new Effect(false /*isInput*/, handle)) {}
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
Return<Result> VirtualizerEffect::init() {
@@ -69,10 +62,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> VirtualizerEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> VirtualizerEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> VirtualizerEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> VirtualizerEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> VirtualizerEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> VirtualizerEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> VirtualizerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -92,10 +107,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> VirtualizerEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> VirtualizerEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -117,10 +128,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> VirtualizerEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> VirtualizerEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
@@ -192,43 +199,117 @@
return mEffect->getIntegerParam(VIRTUALIZER_PARAM_STRENGTH, _hidl_cb);
}
-Return<void> VirtualizerEffect::getVirtualSpeakerAngles(AudioChannelBitfield mask,
- AudioDevice device,
- getVirtualSpeakerAngles_cb _hidl_cb) {
- uint32_t channelCount =
- audio_channel_count_from_out_mask(static_cast<audio_channel_mask_t>(mask));
+Return<void> VirtualizerEffect::getVirtualSpeakerAngles(
+#if MAJOR_VERSION <= 6
+ AudioChannelBitfield mask, AudioDevice device, getVirtualSpeakerAngles_cb _hidl_cb) {
+ audio_channel_mask_t halChannelMask = static_cast<audio_channel_mask_t>(mask);
+ audio_devices_t halDeviceType = static_cast<audio_devices_t>(device);
+#else
+ const AudioChannelMask& mask, const DeviceAddress& device,
+ getVirtualSpeakerAngles_cb _hidl_cb) {
+ audio_channel_mask_t halChannelMask;
+ if (status_t status = HidlUtils::audioChannelMaskToHal(mask, &halChannelMask);
+ status != NO_ERROR) {
+ _hidl_cb(mEffect->analyzeStatus(__func__, "audioChannelMaskToHal",
+ Effect::sContextConversion, status),
+ SpeakerAngles{});
+ return Void();
+ }
+ audio_devices_t halDeviceType;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+ if (status_t status = HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress);
+ status != NO_ERROR) {
+ _hidl_cb(mEffect->analyzeStatus(__func__, "deviceAddressToHal", Effect::sContextConversion,
+ status),
+ SpeakerAngles{});
+ return Void();
+ }
+#endif
+ uint32_t channelCount = audio_channel_count_from_out_mask(halChannelMask);
size_t halSpeakerAnglesSize = sizeof(int32_t) * 3 * channelCount;
- uint32_t halParam[3] = {VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES,
- static_cast<audio_channel_mask_t>(mask),
- static_cast<audio_devices_t>(device)};
- hidl_vec<SpeakerAngle> speakerAngles;
+ uint32_t halParam[3] = {VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES, halChannelMask,
+ halDeviceType};
+ SpeakerAngles speakerAngles;
+ status_t status = NO_ERROR;
Result retval = mEffect->getParameterImpl(
- sizeof(halParam), halParam, halSpeakerAnglesSize,
- [&](uint32_t valueSize, const void* valueData) {
- if (valueSize > halSpeakerAnglesSize) {
- valueSize = halSpeakerAnglesSize;
- } else if (valueSize < halSpeakerAnglesSize) {
- channelCount = valueSize / (sizeof(int32_t) * 3);
- }
- speakerAnglesFromHal(reinterpret_cast<const int32_t*>(valueData), channelCount,
- speakerAngles);
- });
+ sizeof(halParam), halParam, halSpeakerAnglesSize,
+ [&](uint32_t valueSize, const void* valueData) {
+ if (valueSize < halSpeakerAnglesSize) {
+ channelCount = valueSize / (sizeof(int32_t) * 3);
+ }
+ status = speakerAnglesFromHal(reinterpret_cast<const int32_t*>(valueData),
+ channelCount, speakerAngles);
+ });
+ if (retval == Result::OK) {
+ retval = mEffect->analyzeStatus(__func__, "speakerAnglesFromHal", "", status);
+ }
_hidl_cb(retval, speakerAngles);
return Void();
}
-Return<Result> VirtualizerEffect::forceVirtualizationMode(AudioDevice device) {
- return mEffect->setParam(VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE,
- static_cast<audio_devices_t>(device));
-}
-
Return<void> VirtualizerEffect::getVirtualizationMode(getVirtualizationMode_cb _hidl_cb) {
uint32_t halMode = 0;
Result retval = mEffect->getParam(VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE, halMode);
+#if MAJOR_VERSION <= 6
_hidl_cb(retval, AudioDevice(halMode));
+#else
+ DeviceAddress device;
+ (void)HidlUtils::deviceAddressFromHal(static_cast<audio_devices_t>(halMode), nullptr, &device);
+ _hidl_cb(retval, device);
+#endif
return Void();
}
+Return<Result> VirtualizerEffect::forceVirtualizationMode(
+#if MAJOR_VERSION <= 6
+ AudioDevice device) {
+ audio_devices_t halDeviceType = static_cast<audio_devices_t>(device);
+#else
+ const DeviceAddress& device) {
+ audio_devices_t halDeviceType;
+ char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+ (void)HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress);
+#endif
+ return mEffect->setParam(VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE, halDeviceType);
+}
+
+#if MAJOR_VERSION <= 6
+// static
+status_t VirtualizerEffect::speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
+ hidl_vec<SpeakerAngle>& speakerAngles) {
+ speakerAngles.resize(channelCount);
+ for (uint32_t i = 0; i < channelCount; ++i) {
+ speakerAngles[i].mask = AudioChannelBitfield(*halAngles++);
+ speakerAngles[i].azimuth = *halAngles++;
+ speakerAngles[i].elevation = *halAngles++;
+ }
+ return NO_ERROR;
+}
+#else
+static int compare_channels(const void* lhs, const void* rhs) {
+ return *(int32_t*)lhs - *(int32_t*)rhs;
+}
+
+// static
+status_t VirtualizerEffect::speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
+ SpeakerAngles& speakerAngles) {
+ speakerAngles.azimuth.resize(channelCount);
+ speakerAngles.elevation.resize(channelCount);
+ int32_t halAnglesSorted[channelCount * 3];
+ memcpy(halAnglesSorted, halAngles, sizeof(halAnglesSorted));
+ // Ensure that channels are ordered from LSb to MSb.
+ qsort(halAnglesSorted, channelCount, sizeof(int32_t) * 3, compare_channels);
+ audio_channel_mask_t halMask = AUDIO_CHANNEL_NONE;
+ int32_t* halAnglesPtr = halAnglesSorted;
+ for (uint32_t i = 0; i < channelCount; ++i) {
+ halMask = static_cast<audio_channel_mask_t>(halMask | *halAnglesPtr++);
+ speakerAngles.azimuth[i] = *halAnglesPtr++;
+ speakerAngles.elevation[i] = *halAnglesPtr++;
+ }
+ return HidlUtils::audioChannelMaskFromHal(halMask, false /*isInput*/, &speakerAngles.mask);
+}
+#endif
+
} // namespace implementation
} // namespace CPP_VERSION
} // namespace effect
diff --git a/audio/effect/all-versions/default/VirtualizerEffect.h b/audio/effect/all-versions/default/VirtualizerEffect.h
index c630b2e..3ed06d1 100644
--- a/audio/effect/all-versions/default/VirtualizerEffect.h
+++ b/audio/effect/all-versions/default/VirtualizerEffect.h
@@ -39,7 +39,9 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
+#if MAJOR_VERSION <= 6
using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioChannelBitfield;
+#endif
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::effect::CPP_VERSION;
@@ -54,7 +56,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -62,14 +72,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -96,18 +104,27 @@
Return<bool> isStrengthSupported() override;
Return<Result> setStrength(uint16_t strength) override;
Return<void> getStrength(getStrength_cb _hidl_cb) override;
+ Return<void> getVirtualizationMode(getVirtualizationMode_cb _hidl_cb) override;
+#if MAJOR_VERSION <= 6
Return<void> getVirtualSpeakerAngles(AudioChannelBitfield mask, AudioDevice device,
getVirtualSpeakerAngles_cb _hidl_cb) override;
Return<Result> forceVirtualizationMode(AudioDevice device) override;
- Return<void> getVirtualizationMode(getVirtualizationMode_cb _hidl_cb) override;
+#else
+ Return<void> getVirtualSpeakerAngles(const AudioChannelMask& mask, const DeviceAddress& device,
+ getVirtualSpeakerAngles_cb _hidl_cb) override;
+ Return<Result> forceVirtualizationMode(const DeviceAddress& device) override;
+#endif
- private:
+ private:
sp<Effect> mEffect;
- virtual ~VirtualizerEffect();
+ virtual ~VirtualizerEffect() = default;
- void speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
- hidl_vec<SpeakerAngle>& speakerAngles);
+#if MAJOR_VERSION <= 6
+ using SpeakerAngles = hidl_vec<SpeakerAngle>;
+#endif
+ static status_t speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
+ SpeakerAngles& speakerAngles);
};
} // namespace implementation
diff --git a/audio/effect/all-versions/default/VisualizerEffect.cpp b/audio/effect/all-versions/default/VisualizerEffect.cpp
index ae533bf..80c8637 100644
--- a/audio/effect/all-versions/default/VisualizerEffect.cpp
+++ b/audio/effect/all-versions/default/VisualizerEffect.cpp
@@ -31,9 +31,9 @@
namespace implementation {
VisualizerEffect::VisualizerEffect(effect_handle_t handle)
- : mEffect(new Effect(handle)), mCaptureSize(0), mMeasurementMode(MeasurementMode::NONE) {}
-
-VisualizerEffect::~VisualizerEffect() {}
+ : mEffect(new Effect(false /*isInput*/, handle)),
+ mCaptureSize(0),
+ mMeasurementMode(MeasurementMode::NONE) {}
// Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
Return<Result> VisualizerEffect::init() {
@@ -58,10 +58,32 @@
return mEffect->disable();
}
+#if MAJOR_VERSION <= 6
+Return<Result> VisualizerEffect::setAudioSource(AudioSource source) {
+ return mEffect->setAudioSource(source);
+}
+
Return<Result> VisualizerEffect::setDevice(AudioDeviceBitfield device) {
return mEffect->setDevice(device);
}
+Return<Result> VisualizerEffect::setInputDevice(AudioDeviceBitfield device) {
+ return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> VisualizerEffect::setAudioSource(const AudioSource& source) {
+ return mEffect->setAudioSource(source);
+}
+
+Return<Result> VisualizerEffect::setDevice(const DeviceAddress& device) {
+ return mEffect->setDevice(device);
+}
+
+Return<Result> VisualizerEffect::setInputDevice(const DeviceAddress& device) {
+ return mEffect->setInputDevice(device);
+}
+#endif
+
Return<void> VisualizerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) {
return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -81,10 +103,6 @@
return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
}
-Return<Result> VisualizerEffect::setInputDevice(AudioDeviceBitfield device) {
- return mEffect->setInputDevice(device);
-}
-
Return<void> VisualizerEffect::getConfig(getConfig_cb _hidl_cb) {
return mEffect->getConfig(_hidl_cb);
}
@@ -106,10 +124,6 @@
return mEffect->setAuxChannelsConfig(config);
}
-Return<Result> VisualizerEffect::setAudioSource(AudioSource source) {
- return mEffect->setAudioSource(source);
-}
-
Return<Result> VisualizerEffect::offload(const EffectOffloadParameter& param) {
return mEffect->offload(param);
}
diff --git a/audio/effect/all-versions/default/VisualizerEffect.h b/audio/effect/all-versions/default/VisualizerEffect.h
index 315f844..3ae4b08 100644
--- a/audio/effect/all-versions/default/VisualizerEffect.h
+++ b/audio/effect/all-versions/default/VisualizerEffect.h
@@ -53,7 +53,15 @@
Return<Result> reset() override;
Return<Result> enable() override;
Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+ Return<Result> setAudioSource(AudioSource source) override;
Return<Result> setDevice(AudioDeviceBitfield device) override;
+ Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+ Return<Result> setAudioSource(const AudioSource& source) override;
+ Return<Result> setDevice(const DeviceAddress& device) override;
+ Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
setAndGetVolume_cb _hidl_cb) override;
Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
Return<Result> setConfigReverse(
const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
- Return<Result> setInputDevice(AudioDeviceBitfield device) override;
Return<void> getConfig(getConfig_cb _hidl_cb) override;
Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
Return<void> getSupportedAuxChannelsConfigs(
uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
- Return<Result> setAudioSource(AudioSource source) override;
Return<Result> offload(const EffectOffloadParameter& param) override;
Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -107,7 +113,7 @@
uint16_t mCaptureSize;
MeasurementMode mMeasurementMode;
- virtual ~VisualizerEffect();
+ virtual ~VisualizerEffect() = default;
};
} // namespace implementation
diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
index 199a8a5..d39fbcd 100644
--- a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
@@ -263,7 +263,7 @@
static_cast<audio_channel_mask_t>(currentConfig.outputCfg.channels));
#else
*channelCount = android::audio::policy::configuration::V7_0::getChannelCount(
- currentConfig.outputCfg.channels);
+ currentConfig.outputCfg.base.channelMask);
ASSERT_NE(*channelCount, 0);
#endif
}
@@ -353,8 +353,14 @@
}
inline bool operator==(const EffectBufferConfig& lhs, const EffectBufferConfig& rhs) {
- return lhs.buffer == rhs.buffer && lhs.samplingRateHz == rhs.samplingRateHz &&
- lhs.channels == rhs.channels && lhs.format == rhs.format &&
+ return lhs.buffer == rhs.buffer &&
+#if MAJOR_VERSION <= 6
+ lhs.samplingRateHz == rhs.samplingRateHz && lhs.channels == rhs.channels &&
+ lhs.format == rhs.format &&
+#else
+ lhs.base.sampleRateHz == rhs.base.sampleRateHz &&
+ lhs.base.channelMask == rhs.base.channelMask && lhs.base.format == rhs.base.format &&
+#endif
lhs.accessMode == rhs.accessMode && lhs.mask == rhs.mask;
}
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
index 092038b..73fe06c 100644
--- a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
@@ -57,14 +57,14 @@
if (audioConfig.getDiscriminator() ==
V2_0::AudioConfiguration::hidl_discriminator::pcmConfig) {
- audioConfig_2_1.pcmConfig() = {
+ audioConfig_2_1.pcmConfig({
.sampleRate =
static_cast<SampleRate>(audioConfig.pcmConfig().sampleRate),
.channelMode = audioConfig.pcmConfig().channelMode,
.bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
- .dataIntervalUs = 0};
+ .dataIntervalUs = 0});
} else {
- audioConfig_2_1.codecConfig() = audioConfig.codecConfig();
+ audioConfig_2_1.codecConfig(audioConfig.codecConfig());
}
return startSession_2_1(hostIf, audioConfig_2_1, _hidl_cb);
diff --git a/common/TEST_MAPPING b/common/TEST_MAPPING
new file mode 100644
index 0000000..7dd29e5
--- /dev/null
+++ b/common/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libaidlcommonsupport_test"
+ }
+ ]
+}
diff --git a/common/support/Android.bp b/common/support/Android.bp
new file mode 100644
index 0000000..3bb4804
--- /dev/null
+++ b/common/support/Android.bp
@@ -0,0 +1,27 @@
+cc_library_static {
+ name: "libaidlcommonsupport",
+ vendor_available: true,
+ host_supported: true,
+ defaults: ["libbinder_ndk_host_user"],
+ srcs: ["NativeHandle.cpp"],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "android.hardware.common-unstable-ndk_platform",
+ "libcutils",
+ ],
+}
+
+cc_test {
+ name: "libaidlcommonsupport_test",
+ host_supported: true,
+ defaults: ["libbinder_ndk_host_user"],
+ srcs: ["test.cpp"],
+ static_libs: [
+ "libaidlcommonsupport",
+ ],
+ shared_libs: [
+ "android.hardware.common-unstable-ndk_platform",
+ "libcutils",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/common/support/NativeHandle.cpp b/common/support/NativeHandle.cpp
new file mode 100644
index 0000000..321d7a8
--- /dev/null
+++ b/common/support/NativeHandle.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <aidlcommonsupport/NativeHandle.h>
+
+#include <fcntl.h>
+
+namespace android {
+
+using aidl::android::hardware::common::NativeHandle;
+
+static native_handle_t* fromAidl(const NativeHandle& handle, bool doDup) {
+ native_handle_t* to = native_handle_create(handle.fds.size(), handle.ints.size());
+ if (!to) return nullptr;
+
+ for (size_t i = 0; i < handle.fds.size(); i++) {
+ int fd = handle.fds[i].get();
+ to->data[i] = doDup ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd;
+ }
+ memcpy(to->data + handle.fds.size(), handle.ints.data(), handle.ints.size() * sizeof(int));
+ return to;
+}
+
+native_handle_t* makeFromAidl(const NativeHandle& handle) {
+ return fromAidl(handle, false /* doDup */);
+}
+native_handle_t* dupFromAidl(const NativeHandle& handle) {
+ return fromAidl(handle, true /* doDup */);
+}
+
+static NativeHandle toAidl(const native_handle_t* handle, bool doDup) {
+ NativeHandle to;
+
+ to.fds = std::vector<ndk::ScopedFileDescriptor>(handle->numFds);
+ for (size_t i = 0; i < handle->numFds; i++) {
+ int fd = handle->data[i];
+ to.fds.at(i).set(doDup ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd);
+ }
+
+ to.ints = std::vector<int32_t>(handle->data + handle->numFds,
+ handle->data + handle->numFds + handle->numInts);
+ return to;
+}
+
+NativeHandle makeToAidl(const native_handle_t* handle) {
+ return toAidl(handle, false /* doDup */);
+}
+
+NativeHandle dupToAidl(const native_handle_t* handle) {
+ return toAidl(handle, true /* doDup */);
+}
+
+} // namespace android
diff --git a/common/support/include/aidlcommonsupport/NativeHandle.h b/common/support/include/aidlcommonsupport/NativeHandle.h
new file mode 100644
index 0000000..10eecba
--- /dev/null
+++ b/common/support/include/aidlcommonsupport/NativeHandle.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/common/NativeHandle.h>
+#include <cutils/native_handle.h>
+
+namespace android {
+
+/**
+ * Creates a libcutils native handle from an AIDL native handle, but it does not
+ * dup internally, so it will contain the same FDs as the handle itself. The
+ * result should be deleted with native_handle_delete.
+ */
+native_handle_t* makeFromAidl(const aidl::android::hardware::common::NativeHandle& handle);
+
+/**
+ * Creates a libcutils native handle from an AIDL native handle with a dup
+ * internally. It's expected the handle is cleaned up with native_handle_close
+ * and native_handle_delete.
+ */
+native_handle_t* dupFromAidl(const aidl::android::hardware::common::NativeHandle& handle);
+
+/**
+ * Creates an AIDL native handle from a libcutils native handle, but does not
+ * dup internally, so the result will contain the same FDs as the handle itself.
+ *
+ * Warning: this passes ownership of the FDs to the ScopedFileDescriptor
+ * objects.
+ */
+aidl::android::hardware::common::NativeHandle makeToAidl(const native_handle_t* handle);
+
+/**
+ * Creates an AIDL native handle from a libcutils native handle with a dup
+ * internally.
+ */
+aidl::android::hardware::common::NativeHandle dupToAidl(const native_handle_t* handle);
+
+} // namespace android
diff --git a/common/support/test.cpp b/common/support/test.cpp
new file mode 100644
index 0000000..2359277
--- /dev/null
+++ b/common/support/test.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+using aidl::android::hardware::common::NativeHandle;
+using ndk::ScopedFileDescriptor;
+
+static void checkEq(const NativeHandle& aidl, native_handle_t* libcutils, bool exceptFds) {
+ ASSERT_NE(libcutils, nullptr);
+ ASSERT_EQ(libcutils->numFds, aidl.fds.size());
+
+ for (size_t i = 0; i < libcutils->numFds; i++) {
+ int afd = aidl.fds.at(i).get();
+ int lfd = libcutils->data[i];
+
+ EXPECT_GE(afd, 0) << "Invalid fd at index " << i;
+ EXPECT_GE(lfd, 0) << "Invalid fd at index " << i;
+
+ if (exceptFds) {
+ EXPECT_NE(afd, lfd) << "Index matched at " << i << " but should be dup'd fd";
+ } else {
+ EXPECT_EQ(afd, lfd) << "Index mismatched at " << i << " but should be same fd";
+ }
+ }
+
+ ASSERT_EQ(libcutils->numInts, aidl.ints.size());
+
+ for (size_t i = 0; i < libcutils->numInts; i++) {
+ int afd = aidl.ints.at(i);
+ int lfd = libcutils->data[libcutils->numFds + i];
+
+ EXPECT_EQ(afd, lfd) << "Index mismatch at " << i;
+ }
+}
+
+static NativeHandle makeTestAidlHandle() {
+ NativeHandle handle = {
+ .fds = std::vector<ScopedFileDescriptor>(2),
+ .ints = {1, 2, 3, 4},
+ };
+ handle.fds[0].set(dup(0));
+ handle.fds[1].set(dup(0));
+ return handle;
+}
+
+TEST(ConvertNativeHandle, MakeFromAidlEmpty) {
+ NativeHandle handle;
+ native_handle_t* to = makeFromAidl(handle);
+ checkEq(handle, to, false /*exceptFds*/);
+ // no native_handle_close b/c fds are owned by NativeHandle
+ EXPECT_EQ(0, native_handle_delete(to));
+}
+
+TEST(ConvertNativeHandle, MakeFromAidl) {
+ NativeHandle handle = makeTestAidlHandle();
+ native_handle_t* to = makeFromAidl(handle);
+ checkEq(handle, to, false /*exceptFds*/);
+ // no native_handle_close b/c fds are owned by NativeHandle
+ EXPECT_EQ(0, native_handle_delete(to));
+}
+
+TEST(ConvertNativeHandle, DupFromAidlEmpty) {
+ NativeHandle handle;
+ native_handle_t* to = dupFromAidl(handle);
+ checkEq(handle, to, true /*exceptFds*/);
+ EXPECT_EQ(0, native_handle_close(to));
+ EXPECT_EQ(0, native_handle_delete(to));
+}
+
+TEST(ConvertNativeHandle, DupFromAidl) {
+ NativeHandle handle = makeTestAidlHandle();
+ native_handle_t* to = dupFromAidl(handle);
+ checkEq(handle, to, true /*exceptFds*/);
+ EXPECT_EQ(0, native_handle_close(to));
+ EXPECT_EQ(0, native_handle_delete(to));
+}
+
+static native_handle_t* makeTestLibcutilsHandle() {
+ native_handle_t* handle = native_handle_create(2, 4);
+ handle->data[0] = dup(0);
+ handle->data[1] = dup(0);
+ handle->data[2] = 1;
+ handle->data[3] = 2;
+ handle->data[4] = 3;
+ handle->data[5] = 4;
+ return handle;
+}
+
+TEST(ConvertNativeHandle, MakeToAidlEmpty) {
+ native_handle_t* handle = native_handle_create(0, 0);
+ NativeHandle to = makeToAidl(handle);
+ checkEq(to, handle, false /*exceptFds*/);
+ // no native_handle_close b/c fds are owned by NativeHandle now
+ EXPECT_EQ(0, native_handle_delete(handle));
+}
+
+TEST(ConvertNativeHandle, MakeToAidl) {
+ native_handle_t* handle = makeTestLibcutilsHandle();
+ NativeHandle to = makeToAidl(handle);
+ checkEq(to, handle, false /*exceptFds*/);
+ // no native_handle_close b/c fds are owned by NativeHandle now
+ EXPECT_EQ(0, native_handle_delete(handle));
+}
+
+TEST(ConvertNativeHandle, DupToAidlEmpty) {
+ native_handle_t* handle = native_handle_create(0, 0);
+ NativeHandle to = dupToAidl(handle);
+ checkEq(to, handle, true /*exceptFds*/);
+ EXPECT_EQ(0, native_handle_close(handle));
+ EXPECT_EQ(0, native_handle_delete(handle));
+}
+
+TEST(ConvertNativeHandle, DupToAidl) {
+ native_handle_t* handle = makeTestLibcutilsHandle();
+ NativeHandle to = dupToAidl(handle);
+ checkEq(to, handle, true /*exceptFds*/);
+ EXPECT_EQ(0, native_handle_close(handle));
+ EXPECT_EQ(0, native_handle_delete(handle));
+}
+
+} // namespace android
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index b2a815f..de73201 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -60,5 +60,6 @@
kernel_configs: [
"kernel_config_current_4.19",
"kernel_config_current_5.4",
+ "kernel_config_current_5.10",
],
}
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 1957f8c..f086a6e 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -260,11 +260,20 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.identity</name>
+ <version>1-2</version>
<interface>
<name>IIdentityCredentialStore</name>
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.oemlock</name>
+ <version>1</version>
+ <interface>
+ <name>IOemLock</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="hidl" optional="true">
<name>android.hardware.ir</name>
<version>1.0</version>
@@ -299,7 +308,7 @@
</interface>
</hal>
<hal format="aidl" optional="true">
- <name>android.hardware.keymint</name>
+ <name>android.hardware.security.keymint</name>
<interface>
<name>IKeyMintDevice</name>
<instance>default</instance>
@@ -334,6 +343,13 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.memtrack</name>
+ <interface>
+ <name>IMemtrack</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="hidl" optional="true">
<name>android.hardware.memtrack</name>
<version>1.0</version>
@@ -524,14 +540,6 @@
</interface>
</hal>
<hal format="hidl" optional="true">
- <name>android.hardware.vr</name>
- <version>1.0</version>
- <interface>
- <name>IVr</name>
- <instance>default</instance>
- </interface>
- </hal>
- <hal format="hidl" optional="true">
<name>android.hardware.weaver</name>
<version>1.0</version>
<interface>
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 8077f08..50f282f 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -19,6 +19,8 @@
#include <algorithm>
#include <regex>
#include <thread>
+#include <unordered_map>
+#include <utility>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -285,6 +287,59 @@
}
}
+TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute_2_4_ConfigsInAGroupDifferOnlyByVsyncPeriod) {
+ struct Resolution {
+ int32_t width, height;
+ };
+ struct Dpi {
+ int32_t x, y;
+ };
+ for (const auto& display : mDisplays) {
+ std::vector<Config> configs = mComposerClient->getDisplayConfigs(display.get());
+ std::unordered_map<int32_t, Resolution> configGroupToResolutionMap;
+ std::unordered_map<int32_t, Dpi> configGroupToDpiMap;
+ for (auto config : configs) {
+ const auto configGroup = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::CONFIG_GROUP);
+ const auto width = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::WIDTH);
+ const auto height = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::HEIGHT);
+ if (configGroupToResolutionMap.find(configGroup) == configGroupToResolutionMap.end()) {
+ configGroupToResolutionMap[configGroup] = {width, height};
+ }
+ EXPECT_EQ(configGroupToResolutionMap[configGroup].width, width);
+ EXPECT_EQ(configGroupToResolutionMap[configGroup].height, height);
+
+ int32_t dpiX = -1;
+ mComposerClient->getRaw()->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::DPI_X,
+ [&](const auto& tmpError, const auto& value) {
+ if (tmpError == Error::NONE) {
+ dpiX = value;
+ }
+ });
+ int32_t dpiY = -1;
+ mComposerClient->getRaw()->getDisplayAttribute_2_4(
+ display.get(), config, IComposerClient::Attribute::DPI_Y,
+ [&](const auto& tmpError, const auto& value) {
+ if (tmpError == Error::NONE) {
+ dpiY = value;
+ }
+ });
+ if (dpiX == -1 && dpiY == -1) {
+ continue;
+ }
+
+ if (configGroupToDpiMap.find(configGroup) == configGroupToDpiMap.end()) {
+ configGroupToDpiMap[configGroup] = {dpiX, dpiY};
+ }
+ EXPECT_EQ(configGroupToDpiMap[configGroup].x, dpiX);
+ EXPECT_EQ(configGroupToDpiMap[configGroup].y, dpiY);
+ }
+ }
+}
+
TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod_BadDisplay) {
VsyncPeriodNanos vsyncPeriodNanos;
EXPECT_EQ(Error::BAD_DISPLAY,
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
index 2eb0faa..7f342d0 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -1,3 +1,67 @@
+cc_library_static {
+ name: "android.hardware.identity-libeic-hal-common",
+ vendor_available: true,
+ srcs: [
+ "common/IdentityCredential.cpp",
+ "common/IdentityCredentialStore.cpp",
+ "common/WritableIdentityCredential.cpp",
+ ],
+ export_include_dirs: [
+ "common",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "liblog",
+ "libcrypto",
+ "libbinder_ndk",
+ "libkeymaster_messages",
+ ],
+ static_libs: [
+ "libbase",
+ "libcppbor",
+ "libutils",
+ "libsoft_attestation_cert",
+ "libkeymaster_portable",
+ "libsoft_attestation_cert",
+ "libpuresoftkeymasterdevice",
+ "android.hardware.identity-support-lib",
+ "android.hardware.identity-ndk_platform",
+ "android.hardware.keymaster-ndk_platform",
+ ],
+}
+
+cc_library_static {
+ name: "android.hardware.identity-libeic-library",
+ vendor_available: true,
+ srcs: [
+ "libeic/EicCbor.c",
+ "libeic/EicPresentation.c",
+ "libeic/EicProvisioning.c",
+ "EicOpsImpl.cc",
+ ],
+ export_include_dirs: [
+ "libeic",
+ ],
+ cflags: [
+ "-DEIC_COMPILATION",
+ "-Wall",
+ "-Wextra",
+ "-DEIC_DEBUG",
+ // Allow using C2x extensions such as omitting parameter names
+ "-Wno-c2x-extensions",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ ],
+ static_libs: [
+ "android.hardware.identity-support-lib",
+ ],
+}
+
cc_binary {
name: "android.hardware.identity-service.example",
relative_install_path: "hw",
@@ -7,23 +71,30 @@
cflags: [
"-Wall",
"-Wextra",
+ "-g",
],
shared_libs: [
- "libbase",
- "libbinder_ndk",
- "libcppbor",
- "libcrypto",
"liblog",
+ "libcrypto",
+ "libbinder_ndk",
+ "libkeymaster_messages",
+ ],
+ static_libs: [
+ "libbase",
+ "libcppbor",
"libutils",
+ "libsoft_attestation_cert",
+ "libkeymaster_portable",
+ "libsoft_attestation_cert",
+ "libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
"android.hardware.identity-ndk_platform",
"android.hardware.keymaster-ndk_platform",
+ "android.hardware.identity-libeic-hal-common",
+ "android.hardware.identity-libeic-library",
],
srcs: [
- "IdentityCredential.cpp",
- "IdentityCredentialStore.cpp",
- "WritableIdentityCredential.cpp",
- "Util.cpp",
"service.cpp",
+ "FakeSecureHardwareProxy.cpp",
],
}
diff --git a/identity/aidl/default/EicOpsImpl.cc b/identity/aidl/default/EicOpsImpl.cc
new file mode 100644
index 0000000..3f2ec8b
--- /dev/null
+++ b/identity/aidl/default/EicOpsImpl.cc
@@ -0,0 +1,506 @@
+/*
+ * Copyright 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 "EicOpsImpl"
+
+#include <optional>
+#include <tuple>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <string.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <openssl/sha.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include "EicOps.h"
+
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+void* eicMemSet(void* s, int c, size_t n) {
+ return memset(s, c, n);
+}
+
+void* eicMemCpy(void* dest, const void* src, size_t n) {
+ return memcpy(dest, src, n);
+}
+
+size_t eicStrLen(const char* s) {
+ return strlen(s);
+}
+
+int eicCryptoMemCmp(const void* s1, const void* s2, size_t n) {
+ return CRYPTO_memcmp(s1, s2, n);
+}
+
+void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key, size_t keySize) {
+ HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+ HMAC_CTX_init(realCtx);
+ if (HMAC_Init_ex(realCtx, key, keySize, EVP_sha256(), nullptr /* impl */) != 1) {
+ LOG(ERROR) << "Error initializing HMAC_CTX";
+ }
+}
+
+void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data, size_t len) {
+ HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+ if (HMAC_Update(realCtx, data, len) != 1) {
+ LOG(ERROR) << "Error updating HMAC_CTX";
+ }
+}
+
+void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+ HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+ unsigned int size = 0;
+ if (HMAC_Final(realCtx, digest, &size) != 1) {
+ LOG(ERROR) << "Error finalizing HMAC_CTX";
+ }
+ if (size != EIC_SHA256_DIGEST_SIZE) {
+ LOG(ERROR) << "Expected 32 bytes from HMAC_Final, got " << size;
+ }
+}
+
+void eicOpsSha256Init(EicSha256Ctx* ctx) {
+ SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+ SHA256_Init(realCtx);
+}
+
+void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len) {
+ SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+ SHA256_Update(realCtx, data, len);
+}
+
+void eicOpsSha256Final(EicSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+ SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+ SHA256_Final(digest, realCtx);
+}
+
+bool eicOpsRandom(uint8_t* buf, size_t numBytes) {
+ optional<vector<uint8_t>> bytes = ::android::hardware::identity::support::getRandom(numBytes);
+ if (!bytes.has_value()) {
+ return false;
+ }
+ memcpy(buf, bytes.value().data(), numBytes);
+ return true;
+}
+
+bool eicOpsEncryptAes128Gcm(
+ const uint8_t* key, // Must be 16 bytes
+ const uint8_t* nonce, // Must be 12 bytes
+ const uint8_t* data, // May be NULL if size is 0
+ size_t dataSize,
+ const uint8_t* additionalAuthenticationData, // May be NULL if size is 0
+ size_t additionalAuthenticationDataSize, uint8_t* encryptedData) {
+ vector<uint8_t> cppKey;
+ cppKey.resize(16);
+ memcpy(cppKey.data(), key, 16);
+
+ vector<uint8_t> cppData;
+ cppData.resize(dataSize);
+ if (dataSize > 0) {
+ memcpy(cppData.data(), data, dataSize);
+ }
+
+ vector<uint8_t> cppAAD;
+ cppAAD.resize(additionalAuthenticationDataSize);
+ if (additionalAuthenticationDataSize > 0) {
+ memcpy(cppAAD.data(), additionalAuthenticationData, additionalAuthenticationDataSize);
+ }
+
+ vector<uint8_t> cppNonce;
+ cppNonce.resize(12);
+ memcpy(cppNonce.data(), nonce, 12);
+
+ optional<vector<uint8_t>> cppEncryptedData =
+ android::hardware::identity::support::encryptAes128Gcm(cppKey, cppNonce, cppData,
+ cppAAD);
+ if (!cppEncryptedData.has_value()) {
+ return false;
+ }
+
+ memcpy(encryptedData, cppEncryptedData.value().data(), cppEncryptedData.value().size());
+ return true;
+}
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext in |data| must be of size |encryptedDataSize| - 28.
+//
+// The format of |encryptedData| must be as specified in the
+// encryptAes128Gcm() function.
+bool eicOpsDecryptAes128Gcm(const uint8_t* key, // Must be 16 bytes
+ const uint8_t* encryptedData, size_t encryptedDataSize,
+ const uint8_t* additionalAuthenticationData,
+ size_t additionalAuthenticationDataSize, uint8_t* data) {
+ vector<uint8_t> keyVec;
+ keyVec.resize(16);
+ memcpy(keyVec.data(), key, 16);
+
+ vector<uint8_t> encryptedDataVec;
+ encryptedDataVec.resize(encryptedDataSize);
+ if (encryptedDataSize > 0) {
+ memcpy(encryptedDataVec.data(), encryptedData, encryptedDataSize);
+ }
+
+ vector<uint8_t> aadVec;
+ aadVec.resize(additionalAuthenticationDataSize);
+ if (additionalAuthenticationDataSize > 0) {
+ memcpy(aadVec.data(), additionalAuthenticationData, additionalAuthenticationDataSize);
+ }
+
+ optional<vector<uint8_t>> decryptedDataVec =
+ android::hardware::identity::support::decryptAes128Gcm(keyVec, encryptedDataVec,
+ aadVec);
+ if (!decryptedDataVec.has_value()) {
+ eicDebug("Error decrypting data");
+ return false;
+ }
+ if (decryptedDataVec.value().size() != encryptedDataSize - 28) {
+ eicDebug("Decrypted data is size %zd, expected %zd", decryptedDataVec.value().size(),
+ encryptedDataSize - 28);
+ return false;
+ }
+
+ if (decryptedDataVec.value().size() > 0) {
+ memcpy(data, decryptedDataVec.value().data(), decryptedDataVec.value().size());
+ }
+ return true;
+}
+
+bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]) {
+ optional<vector<uint8_t>> keyPair = android::hardware::identity::support::createEcKeyPair();
+ if (!keyPair) {
+ eicDebug("Error creating EC keypair");
+ return false;
+ }
+ optional<vector<uint8_t>> privKey =
+ android::hardware::identity::support::ecKeyPairGetPrivateKey(keyPair.value());
+ if (!privKey) {
+ eicDebug("Error extracting private key");
+ return false;
+ }
+ if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
+ eicDebug("Private key is not %zd bytes long as expected", (size_t)EIC_P256_PRIV_KEY_SIZE);
+ return false;
+ }
+
+ optional<vector<uint8_t>> pubKey =
+ android::hardware::identity::support::ecKeyPairGetPublicKey(keyPair.value());
+ if (!pubKey) {
+ eicDebug("Error extracting public key");
+ return false;
+ }
+ // ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading 0x04.
+ if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) {
+ eicDebug("Private key is %zd bytes long, expected %zd", pubKey.value().size(),
+ (size_t)EIC_P256_PRIV_KEY_SIZE + 1);
+ return false;
+ }
+
+ memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE);
+ memcpy(publicKey, pubKey.value().data() + 1, EIC_P256_PUB_KEY_SIZE);
+
+ return true;
+}
+
+bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge,
+ size_t challengeSize, const uint8_t* applicationId,
+ size_t applicationIdSize, bool testCredential, uint8_t* cert,
+ size_t* certSize) {
+ vector<uint8_t> challengeVec(challengeSize);
+ memcpy(challengeVec.data(), challenge, challengeSize);
+
+ vector<uint8_t> applicationIdVec(applicationIdSize);
+ memcpy(applicationIdVec.data(), applicationId, applicationIdSize);
+
+ optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> ret =
+ android::hardware::identity::support::createEcKeyPairAndAttestation(
+ challengeVec, applicationIdVec, testCredential);
+ if (!ret) {
+ eicDebug("Error generating CredentialKey and attestation");
+ return false;
+ }
+
+ // Extract certificate chain.
+ vector<uint8_t> flatChain =
+ android::hardware::identity::support::certificateChainJoin(ret.value().second);
+ if (*certSize < flatChain.size()) {
+ eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize,
+ flatChain.size());
+ return false;
+ }
+ memcpy(cert, flatChain.data(), flatChain.size());
+ *certSize = flatChain.size();
+
+ // Extract private key.
+ optional<vector<uint8_t>> privKey =
+ android::hardware::identity::support::ecKeyPairGetPrivateKey(ret.value().first);
+ if (!privKey) {
+ eicDebug("Error extracting private key");
+ return false;
+ }
+ if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
+ eicDebug("Private key is not %zd bytes long as expected", (size_t)EIC_P256_PRIV_KEY_SIZE);
+ return false;
+ }
+
+ memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE);
+
+ return true;
+}
+
+bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
+ const char* issuerName, const char* subjectName, time_t validityNotBefore,
+ time_t validityNotAfter, uint8_t* cert,
+ size_t* certSize) { // inout
+ vector<uint8_t> signingKeyVec(EIC_P256_PRIV_KEY_SIZE);
+ memcpy(signingKeyVec.data(), signingKey, EIC_P256_PRIV_KEY_SIZE);
+
+ vector<uint8_t> pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1);
+ pubKeyVec[0] = 0x04;
+ memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
+
+ std::string serialDecimal = android::base::StringPrintf("%d", serial);
+
+ optional<vector<uint8_t>> certVec =
+ android::hardware::identity::support::ecPublicKeyGenerateCertificate(
+ pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName,
+ validityNotBefore, validityNotAfter);
+ if (!certVec) {
+ eicDebug("Error generating certificate");
+ return false;
+ }
+
+ if (*certSize < certVec.value().size()) {
+ eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize,
+ certVec.value().size());
+ return false;
+ }
+ memcpy(cert, certVec.value().data(), certVec.value().size());
+ *certSize = certVec.value().size();
+
+ return true;
+}
+
+bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE],
+ uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ vector<uint8_t> privKeyVec(EIC_P256_PRIV_KEY_SIZE);
+ memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE);
+
+ vector<uint8_t> digestVec(EIC_SHA256_DIGEST_SIZE);
+ memcpy(digestVec.data(), digestOfData, EIC_SHA256_DIGEST_SIZE);
+
+ optional<vector<uint8_t>> derSignature =
+ android::hardware::identity::support::signEcDsaDigest(privKeyVec, digestVec);
+ if (!derSignature) {
+ eicDebug("Error signing data");
+ return false;
+ }
+
+ ECDSA_SIG* sig;
+ const unsigned char* p = derSignature.value().data();
+ sig = d2i_ECDSA_SIG(nullptr, &p, derSignature.value().size());
+ if (sig == nullptr) {
+ eicDebug("Error decoding DER signature");
+ return false;
+ }
+
+ if (BN_bn2binpad(sig->r, signature, 32) != 32) {
+ eicDebug("Error encoding r");
+ return false;
+ }
+ if (BN_bn2binpad(sig->s, signature + 32, 32) != 32) {
+ eicDebug("Error encoding s");
+ return false;
+ }
+
+ return true;
+}
+
+static const uint8_t hbkTest[16] = {0};
+static const uint8_t hbkReal[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential) {
+ if (testCredential) {
+ return hbkTest;
+ }
+ return hbkReal;
+}
+
+bool eicOpsValidateAuthToken(uint64_t /* challenge */, uint64_t /* secureUserId */,
+ uint64_t /* authenticatorId */, int /* hardwareAuthenticatorType */,
+ uint64_t /* timeStamp */, const uint8_t* /* mac */,
+ size_t /* macSize */, uint64_t /* verificationTokenChallenge */,
+ uint64_t /* verificationTokenTimeStamp */,
+ int /* verificationTokenSecurityLevel */,
+ const uint8_t* /* verificationTokenMac */,
+ size_t /* verificationTokenMacSize */) {
+ // Here's where we would validate the passed-in |authToken| to assure ourselves
+ // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
+ //
+ // However this involves calculating the MAC which requires access to the to
+ // a pre-shared key which we don't have...
+ //
+ return true;
+}
+
+bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize, uint8_t* publicKey,
+ size_t* publicKeySize) {
+ vector<uint8_t> chain;
+ chain.resize(x509CertSize);
+ memcpy(chain.data(), x509Cert, x509CertSize);
+ optional<vector<uint8_t>> res =
+ android::hardware::identity::support::certificateChainGetTopMostKey(chain);
+ if (!res) {
+ return false;
+ }
+ if (res.value().size() > *publicKeySize) {
+ eicDebug("Public key size is %zd but buffer only has room for %zd bytes",
+ res.value().size(), *publicKeySize);
+ return false;
+ }
+ *publicKeySize = res.value().size();
+ memcpy(publicKey, res.value().data(), *publicKeySize);
+ eicDebug("Extracted %zd bytes public key from %zd bytes X.509 cert", *publicKeySize,
+ x509CertSize);
+ return true;
+}
+
+bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert, size_t x509CertSize,
+ const uint8_t* publicKey, size_t publicKeySize) {
+ vector<uint8_t> certVec(x509Cert, x509Cert + x509CertSize);
+ vector<uint8_t> publicKeyVec(publicKey, publicKey + publicKeySize);
+ return android::hardware::identity::support::certificateSignedByPublicKey(certVec,
+ publicKeyVec);
+}
+
+bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize,
+ const uint8_t* signature, size_t signatureSize,
+ const uint8_t* publicKey, size_t publicKeySize) {
+ vector<uint8_t> digestVec(digest, digest + digestSize);
+ vector<uint8_t> signatureVec(signature, signature + signatureSize);
+ vector<uint8_t> publicKeyVec(publicKey, publicKey + publicKeySize);
+
+ vector<uint8_t> derSignature;
+ if (!android::hardware::identity::support::ecdsaSignatureCoseToDer(signatureVec,
+ derSignature)) {
+ LOG(ERROR) << "Error convering signature to DER format";
+ return false;
+ }
+
+ if (!android::hardware::identity::support::checkEcDsaSignature(digestVec, derSignature,
+ publicKeyVec)) {
+ LOG(ERROR) << "Signature check failed";
+ return false;
+ }
+ return true;
+}
+
+bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t privateKey[EIC_P256_PUB_KEY_SIZE],
+ uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]) {
+ vector<uint8_t> pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1);
+ pubKeyVec[0] = 0x04;
+ memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
+
+ vector<uint8_t> privKeyVec(EIC_P256_PRIV_KEY_SIZE);
+ memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE);
+
+ optional<vector<uint8_t>> shared =
+ android::hardware::identity::support::ecdh(pubKeyVec, privKeyVec);
+ if (!shared) {
+ LOG(ERROR) << "Error performing ECDH";
+ return false;
+ }
+ if (shared.value().size() != EIC_P256_COORDINATE_SIZE) {
+ LOG(ERROR) << "Unexpected size of shared secret " << shared.value().size() << " expected "
+ << EIC_P256_COORDINATE_SIZE << " bytes";
+ return false;
+ }
+ memcpy(sharedSecret, shared.value().data(), EIC_P256_COORDINATE_SIZE);
+ return true;
+}
+
+bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize, const uint8_t* salt,
+ size_t saltSize, const uint8_t* info, size_t infoSize, uint8_t* output,
+ size_t outputSize) {
+ vector<uint8_t> sharedSecretVec(sharedSecretSize);
+ memcpy(sharedSecretVec.data(), sharedSecret, sharedSecretSize);
+ vector<uint8_t> saltVec(saltSize);
+ memcpy(saltVec.data(), salt, saltSize);
+ vector<uint8_t> infoVec(infoSize);
+ memcpy(infoVec.data(), info, infoSize);
+
+ optional<vector<uint8_t>> result = android::hardware::identity::support::hkdf(
+ sharedSecretVec, saltVec, infoVec, outputSize);
+ if (!result) {
+ LOG(ERROR) << "Error performing HKDF";
+ return false;
+ }
+ if (result.value().size() != outputSize) {
+ LOG(ERROR) << "Unexpected size of HKDF " << result.value().size() << " expected "
+ << outputSize;
+ return false;
+ }
+ memcpy(output, result.value().data(), outputSize);
+ return true;
+}
+
+#ifdef EIC_DEBUG
+
+void eicPrint(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+
+void eicHexdump(const char* message, const uint8_t* data, size_t dataSize) {
+ vector<uint8_t> dataVec(dataSize);
+ memcpy(dataVec.data(), data, dataSize);
+ android::hardware::identity::support::hexdump(message, dataVec);
+}
+
+void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize, size_t maxBStrSize) {
+ vector<uint8_t> cborDataVec(cborDataSize);
+ memcpy(cborDataVec.data(), cborData, cborDataSize);
+ string str =
+ android::hardware::identity::support::cborPrettyPrint(cborDataVec, maxBStrSize, {});
+ fprintf(stderr, "%s\n", str.c_str());
+}
+
+#endif // EIC_DEBUG
diff --git a/identity/aidl/default/EicOpsImpl.h b/identity/aidl/default/EicOpsImpl.h
new file mode 100644
index 0000000..333cdce
--- /dev/null
+++ b/identity/aidl/default/EicOpsImpl.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_OPS_IMPL_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_OPS_IMPL_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Add whatever includes are needed for definitions below.
+//
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Set the following defines to match the implementation of the supplied
+// eicOps*() operations. See EicOps.h for details.
+//
+
+#define EIC_SHA256_CONTEXT_SIZE sizeof(SHA256_CTX)
+
+#define EIC_HMAC_SHA256_CONTEXT_SIZE sizeof(HMAC_CTX)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EMBEDDED_IC_H
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.cpp b/identity/aidl/default/FakeSecureHardwareProxy.cpp
new file mode 100644
index 0000000..de6762f
--- /dev/null
+++ b/identity/aidl/default/FakeSecureHardwareProxy.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright 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 "FakeSecureHardwareProxy"
+
+#include "FakeSecureHardwareProxy.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <string.h>
+
+#include <openssl/sha.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include <libeic.h>
+
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+namespace android::hardware::identity {
+
+// ----------------------------------------------------------------------
+
+FakeSecureHardwareProvisioningProxy::FakeSecureHardwareProvisioningProxy() {}
+
+FakeSecureHardwareProvisioningProxy::~FakeSecureHardwareProvisioningProxy() {}
+
+bool FakeSecureHardwareProvisioningProxy::shutdown() {
+ LOG(INFO) << "FakeSecureHardwarePresentationProxy shutdown";
+ return true;
+}
+
+bool FakeSecureHardwareProvisioningProxy::initialize(bool testCredential) {
+ LOG(INFO) << "FakeSecureHardwareProvisioningProxy created, sizeof(EicProvisioning): "
+ << sizeof(EicProvisioning);
+ return eicProvisioningInit(&ctx_, testCredential);
+}
+
+// Returns public key certificate.
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKey(
+ const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
+ uint8_t publicKeyCert[4096];
+ size_t publicKeyCertSize = sizeof publicKeyCert;
+ if (!eicProvisioningCreateCredentialKey(&ctx_, challenge.data(), challenge.size(),
+ applicationId.data(), applicationId.size(),
+ publicKeyCert, &publicKeyCertSize)) {
+ return {};
+ }
+ vector<uint8_t> pubKeyCert(publicKeyCertSize);
+ memcpy(pubKeyCert.data(), publicKeyCert, publicKeyCertSize);
+ return pubKeyCert;
+}
+
+bool FakeSecureHardwareProvisioningProxy::startPersonalization(
+ int accessControlProfileCount, vector<int> entryCounts, const string& docType,
+ size_t expectedProofOfProvisioningSize) {
+ if (!eicProvisioningStartPersonalization(&ctx_, accessControlProfileCount, entryCounts.data(),
+ entryCounts.size(), docType.c_str(),
+ expectedProofOfProvisioningSize)) {
+ return false;
+ }
+ return true;
+}
+
+// Returns MAC (28 bytes).
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+ uint64_t timeoutMillis, uint64_t secureUserId) {
+ vector<uint8_t> mac(28);
+ if (!eicProvisioningAddAccessControlProfile(
+ &ctx_, id, readerCertificate.data(), readerCertificate.size(),
+ userAuthenticationRequired, timeoutMillis, secureUserId, mac.data())) {
+ return {};
+ }
+ return mac;
+}
+
+bool FakeSecureHardwareProvisioningProxy::beginAddEntry(const vector<int>& accessControlProfileIds,
+ const string& nameSpace, const string& name,
+ uint64_t entrySize) {
+ uint8_t scratchSpace[512];
+ return eicProvisioningBeginAddEntry(&ctx_, accessControlProfileIds.data(),
+ accessControlProfileIds.size(), nameSpace.c_str(),
+ name.c_str(), entrySize, scratchSpace, sizeof scratchSpace);
+}
+
+// Returns encryptedContent.
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addEntryValue(
+ const vector<int>& accessControlProfileIds, const string& nameSpace, const string& name,
+ const vector<uint8_t>& content) {
+ vector<uint8_t> eicEncryptedContent;
+ uint8_t scratchSpace[512];
+ eicEncryptedContent.resize(content.size() + 28);
+ if (!eicProvisioningAddEntryValue(
+ &ctx_, accessControlProfileIds.data(), accessControlProfileIds.size(),
+ nameSpace.c_str(), name.c_str(), content.data(), content.size(),
+ eicEncryptedContent.data(), scratchSpace, sizeof scratchSpace)) {
+ return {};
+ }
+ return eicEncryptedContent;
+}
+
+// Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishAddingEntries() {
+ vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+ if (!eicProvisioningFinishAddingEntries(&ctx_, signatureOfToBeSigned.data())) {
+ return {};
+ }
+ return signatureOfToBeSigned;
+}
+
+// Returns encryptedCredentialKeys (80 bytes).
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
+ const string& docType) {
+ vector<uint8_t> encryptedCredentialKeys(80);
+ if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(),
+ encryptedCredentialKeys.data())) {
+ return {};
+ }
+ return encryptedCredentialKeys;
+}
+
+// ----------------------------------------------------------------------
+
+FakeSecureHardwarePresentationProxy::FakeSecureHardwarePresentationProxy() {}
+
+FakeSecureHardwarePresentationProxy::~FakeSecureHardwarePresentationProxy() {}
+
+bool FakeSecureHardwarePresentationProxy::initialize(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) {
+ LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
+ << sizeof(EicPresentation);
+ return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
+ encryptedCredentialKeys.data());
+}
+
+// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+optional<pair<vector<uint8_t>, vector<uint8_t>>>
+FakeSecureHardwarePresentationProxy::generateSigningKeyPair(string docType, time_t now) {
+ uint8_t publicKeyCert[512];
+ size_t publicKeyCertSize = sizeof(publicKeyCert);
+ vector<uint8_t> signingKeyBlob(60);
+
+ if (!eicPresentationGenerateSigningKeyPair(&ctx_, docType.c_str(), now, publicKeyCert,
+ &publicKeyCertSize, signingKeyBlob.data())) {
+ return {};
+ }
+
+ vector<uint8_t> cert;
+ cert.resize(publicKeyCertSize);
+ memcpy(cert.data(), publicKeyCert, publicKeyCertSize);
+
+ return std::make_pair(cert, signingKeyBlob);
+}
+
+// Returns private key
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::createEphemeralKeyPair() {
+ vector<uint8_t> priv(EIC_P256_PRIV_KEY_SIZE);
+ if (!eicPresentationCreateEphemeralKeyPair(&ctx_, priv.data())) {
+ return {};
+ }
+ return priv;
+}
+
+optional<uint64_t> FakeSecureHardwarePresentationProxy::createAuthChallenge() {
+ uint64_t challenge;
+ if (!eicPresentationCreateAuthChallenge(&ctx_, &challenge)) {
+ return {};
+ }
+ return challenge;
+}
+
+bool FakeSecureHardwarePresentationProxy::shutdown() {
+ LOG(INFO) << "FakeSecureHardwarePresentationProxy shutdown";
+ return true;
+}
+
+bool FakeSecureHardwarePresentationProxy::pushReaderCert(const vector<uint8_t>& certX509) {
+ return eicPresentationPushReaderCert(&ctx_, certX509.data(), certX509.size());
+}
+
+bool FakeSecureHardwarePresentationProxy::validateRequestMessage(
+ const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& requestMessage,
+ int coseSignAlg, const vector<uint8_t>& readerSignatureOfToBeSigned) {
+ return eicPresentationValidateRequestMessage(
+ &ctx_, sessionTranscript.data(), sessionTranscript.size(), requestMessage.data(),
+ requestMessage.size(), coseSignAlg, readerSignatureOfToBeSigned.data(),
+ readerSignatureOfToBeSigned.size());
+}
+
+bool FakeSecureHardwarePresentationProxy::setAuthToken(
+ uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+ int hardwareAuthenticatorType, uint64_t timeStamp, const vector<uint8_t>& mac,
+ uint64_t verificationTokenChallenge, uint64_t verificationTokenTimestamp,
+ int verificationTokenSecurityLevel, const vector<uint8_t>& verificationTokenMac) {
+ return eicPresentationSetAuthToken(&ctx_, challenge, secureUserId, authenticatorId,
+ hardwareAuthenticatorType, timeStamp, mac.data(), mac.size(),
+ verificationTokenChallenge, verificationTokenTimestamp,
+ verificationTokenSecurityLevel, verificationTokenMac.data(),
+ verificationTokenMac.size());
+}
+
+optional<bool> FakeSecureHardwarePresentationProxy::validateAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+ int timeoutMillis, uint64_t secureUserId, const vector<uint8_t>& mac) {
+ bool accessGranted = false;
+ if (!eicPresentationValidateAccessControlProfile(&ctx_, id, readerCertificate.data(),
+ readerCertificate.size(),
+ userAuthenticationRequired, timeoutMillis,
+ secureUserId, mac.data(), &accessGranted)) {
+ return {};
+ }
+ return accessGranted;
+}
+
+bool FakeSecureHardwarePresentationProxy::startRetrieveEntries() {
+ return eicPresentationStartRetrieveEntries(&ctx_);
+}
+
+bool FakeSecureHardwarePresentationProxy::calcMacKey(
+ const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& readerEphemeralPublicKey,
+ const vector<uint8_t>& signingKeyBlob, const string& docType,
+ unsigned int numNamespacesWithValues, size_t expectedProofOfProvisioningSize) {
+ if (signingKeyBlob.size() != 60) {
+ eicDebug("Unexpected size %zd of signingKeyBlob, expected 60", signingKeyBlob.size());
+ return false;
+ }
+ return eicPresentationCalcMacKey(&ctx_, sessionTranscript.data(), sessionTranscript.size(),
+ readerEphemeralPublicKey.data(), signingKeyBlob.data(),
+ docType.c_str(), numNamespacesWithValues,
+ expectedProofOfProvisioningSize);
+}
+
+AccessCheckResult FakeSecureHardwarePresentationProxy::startRetrieveEntryValue(
+ const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+ int32_t entrySize, const vector<int32_t>& accessControlProfileIds) {
+ uint8_t scratchSpace[512];
+ EicAccessCheckResult result = eicPresentationStartRetrieveEntryValue(
+ &ctx_, nameSpace.c_str(), name.c_str(), newNamespaceNumEntries, entrySize,
+ accessControlProfileIds.data(), accessControlProfileIds.size(), scratchSpace,
+ sizeof scratchSpace);
+ switch (result) {
+ case EIC_ACCESS_CHECK_RESULT_OK:
+ return AccessCheckResult::kOk;
+ case EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES:
+ return AccessCheckResult::kNoAccessControlProfiles;
+ case EIC_ACCESS_CHECK_RESULT_FAILED:
+ return AccessCheckResult::kFailed;
+ case EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED:
+ return AccessCheckResult::kUserAuthenticationFailed;
+ case EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED:
+ return AccessCheckResult::kReaderAuthenticationFailed;
+ }
+ eicDebug("Unknown result with code %d, returning kFailed", (int)result);
+ return AccessCheckResult::kFailed;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::retrieveEntryValue(
+ const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+ const vector<int32_t>& accessControlProfileIds) {
+ uint8_t scratchSpace[512];
+ vector<uint8_t> content;
+ content.resize(encryptedContent.size() - 28);
+ if (!eicPresentationRetrieveEntryValue(
+ &ctx_, encryptedContent.data(), encryptedContent.size(), content.data(),
+ nameSpace.c_str(), name.c_str(), accessControlProfileIds.data(),
+ accessControlProfileIds.size(), scratchSpace, sizeof scratchSpace)) {
+ return {};
+ }
+ return content;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::finishRetrieval() {
+ vector<uint8_t> mac(32);
+ size_t macSize = 32;
+ if (!eicPresentationFinishRetrieval(&ctx_, mac.data(), &macSize)) {
+ return {};
+ }
+ mac.resize(macSize);
+ return mac;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
+ const string& docType, size_t proofOfDeletionCborSize) {
+ vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+ if (!eicPresentationDeleteCredential(&ctx_, docType.c_str(), proofOfDeletionCborSize,
+ signatureOfToBeSigned.data())) {
+ return {};
+ }
+ return signatureOfToBeSigned;
+}
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.h b/identity/aidl/default/FakeSecureHardwareProxy.h
new file mode 100644
index 0000000..b858dd4
--- /dev/null
+++ b/identity/aidl/default/FakeSecureHardwareProxy.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
+#define ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
+
+#include <libeic/libeic.h>
+
+#include "SecureHardwareProxy.h"
+
+namespace android::hardware::identity {
+
+// This implementation uses libEmbeddedIC in-process.
+//
+class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningProxy {
+ public:
+ FakeSecureHardwareProvisioningProxy();
+ virtual ~FakeSecureHardwareProvisioningProxy();
+
+ bool initialize(bool testCredential) override;
+
+ bool shutdown() override;
+
+ // Returns public key certificate.
+ optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
+ const vector<uint8_t>& applicationId) override;
+
+ bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts,
+ const string& docType,
+ size_t expectedProofOfProvisioningSize) override;
+
+ // Returns MAC (28 bytes).
+ optional<vector<uint8_t>> addAccessControlProfile(int id,
+ const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired,
+ uint64_t timeoutMillis,
+ uint64_t secureUserId) override;
+
+ bool beginAddEntry(const vector<int>& accessControlProfileIds, const string& nameSpace,
+ const string& name, uint64_t entrySize) override;
+
+ // Returns encryptedContent.
+ optional<vector<uint8_t>> addEntryValue(const vector<int>& accessControlProfileIds,
+ const string& nameSpace, const string& name,
+ const vector<uint8_t>& content) override;
+
+ // Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+ optional<vector<uint8_t>> finishAddingEntries() override;
+
+ // Returns encryptedCredentialKeys (80 bytes).
+ optional<vector<uint8_t>> finishGetCredentialData(const string& docType) override;
+
+ protected:
+ EicProvisioning ctx_;
+};
+
+// This implementation uses libEmbeddedIC in-process.
+//
+class FakeSecureHardwarePresentationProxy : public SecureHardwarePresentationProxy {
+ public:
+ FakeSecureHardwarePresentationProxy();
+ virtual ~FakeSecureHardwarePresentationProxy();
+
+ bool initialize(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) override;
+
+ // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+ optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType,
+ time_t now) override;
+
+ // Returns private key
+ optional<vector<uint8_t>> createEphemeralKeyPair() override;
+
+ optional<uint64_t> createAuthChallenge() override;
+
+ bool startRetrieveEntries() override;
+
+ bool setAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+ int hardwareAuthenticatorType, uint64_t timeStamp, const vector<uint8_t>& mac,
+ uint64_t verificationTokenChallenge, uint64_t verificationTokenTimestamp,
+ int verificationTokenSecurityLevel,
+ const vector<uint8_t>& verificationTokenMac) override;
+
+ bool pushReaderCert(const vector<uint8_t>& certX509) override;
+
+ optional<bool> validateAccessControlProfile(int id, const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired, int timeoutMillis,
+ uint64_t secureUserId,
+ const vector<uint8_t>& mac) override;
+
+ bool validateRequestMessage(const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& requestMessage, int coseSignAlg,
+ const vector<uint8_t>& readerSignatureOfToBeSigned) override;
+
+ bool calcMacKey(const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& readerEphemeralPublicKey,
+ const vector<uint8_t>& signingKeyBlob, const string& docType,
+ unsigned int numNamespacesWithValues,
+ size_t expectedProofOfProvisioningSize) override;
+
+ AccessCheckResult startRetrieveEntryValue(
+ const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+ int32_t entrySize, const vector<int32_t>& accessControlProfileIds) override;
+
+ optional<vector<uint8_t>> retrieveEntryValue(
+ const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+ const vector<int32_t>& accessControlProfileIds) override;
+
+ optional<vector<uint8_t>> finishRetrieval() override;
+
+ optional<vector<uint8_t>> deleteCredential(const string& docType,
+ size_t proofOfDeletionCborSize) override;
+
+ bool shutdown() override;
+
+ protected:
+ EicPresentation ctx_;
+};
+
+// Factory implementation.
+//
+class FakeSecureHardwareProxyFactory : public SecureHardwareProxyFactory {
+ public:
+ FakeSecureHardwareProxyFactory() {}
+ virtual ~FakeSecureHardwareProxyFactory() {}
+
+ sp<SecureHardwareProvisioningProxy> createProvisioningProxy() override {
+ return new FakeSecureHardwareProvisioningProxy();
+ }
+
+ sp<SecureHardwarePresentationProxy> createPresentationProxy() override {
+ return new FakeSecureHardwarePresentationProxy();
+ }
+};
+
+} // namespace android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
diff --git a/identity/aidl/default/Util.cpp b/identity/aidl/default/Util.cpp
deleted file mode 100644
index 66b9f13..0000000
--- a/identity/aidl/default/Util.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2019, 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 "Util"
-
-#include "Util.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <string.h>
-
-#include <android-base/logging.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-namespace aidl::android::hardware::identity {
-
-using namespace ::android::hardware::identity;
-
-// This is not a very random HBK but that's OK because this is the SW
-// implementation where it can't be kept secret.
-vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
-
-const vector<uint8_t>& getHardwareBoundKey() {
- return hardwareBoundKey;
-}
-
-vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
- cppbor::Map map;
- map.add("id", profile.id);
-
- if (profile.readerCertificate.encodedCertificate.size() > 0) {
- map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate.encodedCertificate));
- }
-
- if (profile.userAuthenticationRequired) {
- map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
- map.add("timeoutMillis", profile.timeoutMillis);
- map.add("secureUserId", profile.secureUserId);
- }
-
- return map.encode();
-}
-
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
- const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
- vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- return {};
- }
- optional<vector<uint8_t>> macO =
- support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
- if (!macO) {
- return {};
- }
- return macO.value();
-}
-
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& storageKey) {
- vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
- if (profile.mac.size() < support::kAesGcmIvSize) {
- return false;
- }
- vector<uint8_t> nonce =
- vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize);
- optional<vector<uint8_t>> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData);
- if (!mac) {
- return false;
- }
- if (mac.value() != profile.mac) {
- return false;
- }
- return true;
-}
-
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
- const vector<int32_t> accessControlProfileIds) {
- cppbor::Map map;
- map.add("Namespace", nameSpace);
- map.add("Name", name);
-
- cppbor::Array acpIds;
- for (auto id : accessControlProfileIds) {
- acpIds.add(id);
- }
- map.add("AccessControlProfileIds", std::move(acpIds));
- return map.encode();
-}
-
-} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/Util.h b/identity/aidl/default/Util.h
deleted file mode 100644
index 9fccba2..0000000
--- a/identity/aidl/default/Util.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2019, 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_IDENTITY_UTIL_H
-#define ANDROID_HARDWARE_IDENTITY_UTIL_H
-
-#include <aidl/android/hardware/identity/BnIdentityCredential.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <map>
-#include <optional>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <cppbor/cppbor.h>
-
-namespace aidl::android::hardware::identity {
-
-using ::std::optional;
-using ::std::string;
-using ::std::vector;
-
-// Returns the hardware-bound AES-128 key.
-const vector<uint8_t>& getHardwareBoundKey();
-
-// Calculates the MAC for |profile| using |storageKey|.
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
- const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
-
-// Checks authenticity of the MAC in |profile| using |storageKey|.
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& storageKey);
-
-// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
- const vector<int32_t> accessControlProfileIds);
-
-} // namespace aidl::android::hardware::identity
-
-#endif // ANDROID_HARDWARE_IDENTITY_UTIL_H
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/common/IdentityCredential.cpp
similarity index 65%
rename from identity/aidl/default/IdentityCredential.cpp
rename to identity/aidl/default/common/IdentityCredential.cpp
index dfcd4f5..270fcfa 100644
--- a/identity/aidl/default/IdentityCredential.cpp
+++ b/identity/aidl/default/common/IdentityCredential.cpp
@@ -18,7 +18,6 @@
#include "IdentityCredential.h"
#include "IdentityCredentialStore.h"
-#include "Util.h"
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
@@ -30,6 +29,8 @@
#include <cppbor.h>
#include <cppbor_parse.h>
+#include "FakeSecureHardwareProxy.h"
+
namespace aidl::android::hardware::identity {
using ::aidl::android::hardware::keymaster::Timestamp;
@@ -69,40 +70,17 @@
docType_ = docTypeItem->value();
testCredential_ = testCredentialItem->value();
- vector<uint8_t> hardwareBoundKey;
- if (testCredential_) {
- hardwareBoundKey = support::getTestHardwareBoundKey();
- } else {
- hardwareBoundKey = getHardwareBoundKey();
- }
-
const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
- const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> decryptedCredentialKeys =
- support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
- if (!decryptedCredentialKeys) {
- LOG(ERROR) << "Error decrypting CredentialKeys";
+
+ if (encryptedCredentialKeys.size() != 80) {
+ LOG(ERROR) << "Unexpected size for encrypted CredentialKeys";
return IIdentityCredentialStore::STATUS_INVALID_DATA;
}
- auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
- if (dckItem == nullptr) {
- LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
- return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys)) {
+ LOG(ERROR) << "hwProxy->initialize failed";
+ return false;
}
- const cppbor::Array* dckArrayItem = dckItem->asArray();
- if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
- LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
- return IIdentityCredentialStore::STATUS_INVALID_DATA;
- }
- const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
- const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
- if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
- LOG(ERROR) << "CredentialKeys unexpected item types";
- return IIdentityCredentialStore::STATUS_INVALID_DATA;
- }
- storageKey_ = storageKeyItem->value();
- credentialPrivKey_ = credentialPrivKeyItem->value();
return IIdentityCredentialStore::STATUS_OK;
}
@@ -110,12 +88,20 @@
ndk::ScopedAStatus IdentityCredential::deleteCredential(
vector<uint8_t>* outProofOfDeletionSignature) {
cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
- vector<uint8_t> proofOfDeletion = array.encode();
+ vector<uint8_t> proofOfDeletionCbor = array.encode();
+ vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
- optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
- proofOfDeletion, // payload
- {}, // additionalData
- {}); // certificateChain
+ optional<vector<uint8_t>> signatureOfToBeSigned =
+ hwProxy_->deleteCredential(docType_, proofOfDeletionCbor.size());
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfDeletion"));
+ }
+
+ optional<vector<uint8_t>> signature =
+ support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+ proofOfDeletionCbor, // data
+ {}); // certificateChain
if (!signature) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
@@ -126,22 +112,28 @@
}
ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
- optional<vector<uint8_t>> kp = support::createEcKeyPair();
- if (!kp) {
+ optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
+ if (!ephemeralPriv) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key"));
+ }
+ optional<vector<uint8_t>> keyPair = support::ecPrivateKeyToKeyPair(ephemeralPriv.value());
+ if (!keyPair) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key-pair"));
}
// Stash public key of this key-pair for later check in startRetrieval().
- optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
+ optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
if (!publicKey) {
+ LOG(ERROR) << "Error getting public part of ephemeral key pair";
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED,
"Error getting public part of ephemeral key pair"));
}
ephemeralPublicKey_ = publicKey.value();
- *outKeyPair = kp.value();
+ *outKeyPair = keyPair.value();
return ndk::ScopedAStatus::ok();
}
@@ -152,109 +144,15 @@
}
ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
- uint64_t challenge = 0;
- while (challenge == 0) {
- optional<vector<uint8_t>> bytes = support::getRandom(8);
- if (!bytes) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED,
- "Error getting random data for challenge"));
- }
-
- challenge = 0;
- for (size_t n = 0; n < bytes.value().size(); n++) {
- challenge |= ((bytes.value())[n] << (n * 8));
- }
+ optional<uint64_t> challenge = hwProxy_->createAuthChallenge();
+ if (!challenge) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error generating challenge"));
}
-
- *outChallenge = challenge;
- authChallenge_ = challenge;
+ *outChallenge = challenge.value();
return ndk::ScopedAStatus::ok();
}
-// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
-// ahead of time.
-bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& readerCertificateChain) {
- optional<vector<uint8_t>> acpPubKey =
- support::certificateChainGetTopMostKey(profile.readerCertificate.encodedCertificate);
- if (!acpPubKey) {
- LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
- return false;
- }
-
- optional<vector<vector<uint8_t>>> certificatesInChain =
- support::certificateChainSplit(readerCertificateChain);
- if (!certificatesInChain) {
- LOG(ERROR) << "Error splitting readerCertificateChain";
- return false;
- }
- for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
- optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
- if (!certPubKey) {
- LOG(ERROR)
- << "Error extracting public key from certificate in chain presented by reader";
- return false;
- }
- if (acpPubKey.value() == certPubKey.value()) {
- return true;
- }
- }
- return false;
-}
-
-bool checkUserAuthentication(const SecureAccessControlProfile& profile,
- const VerificationToken& verificationToken,
- const HardwareAuthToken& authToken, uint64_t authChallenge) {
- if (profile.secureUserId != authToken.userId) {
- LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
- << ") differs from userId in authToken (" << authToken.userId << ")";
- return false;
- }
-
- if (verificationToken.timestamp.milliSeconds == 0) {
- LOG(ERROR) << "VerificationToken is not set";
- return false;
- }
- if (authToken.timestamp.milliSeconds == 0) {
- LOG(ERROR) << "AuthToken is not set";
- return false;
- }
-
- if (profile.timeoutMillis == 0) {
- if (authToken.challenge == 0) {
- LOG(ERROR) << "No challenge in authToken";
- return false;
- }
-
- if (authToken.challenge != int64_t(authChallenge)) {
- LOG(ERROR) << "Challenge in authToken (" << uint64_t(authToken.challenge) << ") "
- << "doesn't match the challenge we created (" << authChallenge << ")";
- return false;
- }
- return true;
- }
-
- // Timeout-based user auth follows. The verification token conveys what the
- // time is right now in the environment which generated the auth token. This
- // is what makes it possible to do timeout-based checks.
- //
- const Timestamp now = verificationToken.timestamp;
- if (authToken.timestamp.milliSeconds > now.milliSeconds) {
- LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
- << ") is in the future (now: " << now.milliSeconds << ")";
- return false;
- }
- if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
- LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
- << profile.timeoutMillis << " = "
- << (authToken.timestamp.milliSeconds + profile.timeoutMillis)
- << ") is in the past (now: " << now.milliSeconds << ")";
- return false;
- }
- return true;
-}
-
ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
const vector<RequestNamespace>& requestNamespaces) {
requestNamespaces_ = requestNamespaces;
@@ -284,6 +182,7 @@
}
if (numStartRetrievalCalls_ > 0) {
if (sessionTranscript_ != sessionTranscript) {
+ LOG(ERROR) << "Session Transcript changed";
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
"Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
@@ -291,6 +190,40 @@
}
sessionTranscript_ = sessionTranscript;
+ // This resets various state in the TA...
+ if (!hwProxy_->startRetrieveEntries()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
+ }
+
+ optional<vector<uint8_t>> signatureOfToBeSigned;
+ if (readerSignature.size() > 0) {
+ signatureOfToBeSigned = support::coseSignGetSignature(readerSignature);
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error extracting signatureOfToBeSigned from COSE_Sign1"));
+ }
+ }
+
+ // Feed the auth token to secure hardware.
+ if (!hwProxy_->setAuthToken(authToken.challenge, authToken.userId, authToken.authenticatorId,
+ int(authToken.authenticatorType), authToken.timestamp.milliSeconds,
+ authToken.mac, verificationToken_.challenge,
+ verificationToken_.timestamp.milliSeconds,
+ int(verificationToken_.securityLevel), verificationToken_.mac)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Invalid Auth Token"));
+ }
+
+ // We'll be feeding ACPs interleaved with certificates from the reader
+ // certificate chain...
+ vector<SecureAccessControlProfile> remainingAcps = accessControlProfiles;
+
+ // ... and we'll use those ACPs to build up a 32-bit mask indicating which
+ // of the possible 32 ACPs grants access.
+ uint32_t accessControlProfileMask = 0;
+
// If there is a signature, validate that it was made with the top-most key in the
// certificate chain embedded in the COSE_Sign1 structure.
optional<vector<uint8_t>> readerCertificateChain;
@@ -302,45 +235,113 @@
"Unable to get reader certificate chain from COSE_Sign1"));
}
- if (!support::certificateChainValidate(readerCertificateChain.value())) {
+ // First, feed all the reader certificates to the secure hardware. We start
+ // at the end..
+ optional<vector<vector<uint8_t>>> splitCerts =
+ support::certificateChainSplit(readerCertificateChain.value());
+ if (!splitCerts || splitCerts.value().size() == 0) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
- "Error validating reader certificate chain"));
+ "Error splitting certificate chain from COSE_Sign1"));
+ }
+ for (ssize_t n = splitCerts.value().size() - 1; n >= 0; --n) {
+ const vector<uint8_t>& x509Cert = splitCerts.value()[n];
+ if (!hwProxy_->pushReaderCert(x509Cert)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ StringPrintf("Error validating reader certificate %zd", n).c_str()));
+ }
+
+ // If we have ACPs for that particular certificate, send them to the
+ // TA right now...
+ //
+ // Remember in this case certificate equality is done by comparing public keys,
+ // not bitwise comparison of the certificates.
+ //
+ optional<vector<uint8_t>> x509CertPubKey =
+ support::certificateChainGetTopMostKey(x509Cert);
+ if (!x509CertPubKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ StringPrintf("Error getting public key from reader certificate %zd", n)
+ .c_str()));
+ }
+ vector<SecureAccessControlProfile>::iterator it = remainingAcps.begin();
+ while (it != remainingAcps.end()) {
+ const SecureAccessControlProfile& profile = *it;
+ if (profile.readerCertificate.encodedCertificate.size() == 0) {
+ ++it;
+ continue;
+ }
+ optional<vector<uint8_t>> profilePubKey = support::certificateChainGetTopMostKey(
+ profile.readerCertificate.encodedCertificate);
+ if (!profilePubKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public key from profile"));
+ }
+ if (profilePubKey.value() == x509CertPubKey.value()) {
+ optional<bool> res = hwProxy_->validateAccessControlProfile(
+ profile.id, profile.readerCertificate.encodedCertificate,
+ profile.userAuthenticationRequired, profile.timeoutMillis,
+ profile.secureUserId, profile.mac);
+ if (!res) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error validating access control profile"));
+ }
+ if (res.value()) {
+ accessControlProfileMask |= (1 << profile.id);
+ }
+ it = remainingAcps.erase(it);
+ } else {
+ ++it;
+ }
+ }
}
- optional<vector<uint8_t>> readerPublicKey =
- support::certificateChainGetTopMostKey(readerCertificateChain.value());
- if (!readerPublicKey) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
- "Unable to get public key from reader certificate chain"));
- }
-
- const vector<uint8_t>& itemsRequestBytes = itemsRequest;
- vector<uint8_t> encodedReaderAuthentication =
- cppbor::Array()
- .add("ReaderAuthentication")
- .add(std::move(sessionTranscriptItem))
- .add(cppbor::Semantic(24, itemsRequestBytes))
- .encode();
- vector<uint8_t> encodedReaderAuthenticationBytes =
- cppbor::Semantic(24, encodedReaderAuthentication).encode();
- if (!support::coseCheckEcDsaSignature(readerSignature,
- encodedReaderAuthenticationBytes, // detached content
- readerPublicKey.value())) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
- "readerSignature check failed"));
+ // ... then pass the request message and have the TA check it's signed by the
+ // key in last certificate we pushed.
+ if (sessionTranscript.size() > 0 && itemsRequest.size() > 0 && readerSignature.size() > 0) {
+ optional<vector<uint8_t>> tbsSignature = support::coseSignGetSignature(readerSignature);
+ if (!tbsSignature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error extracting toBeSigned from COSE_Sign1"));
+ }
+ optional<int> coseSignAlg = support::coseSignGetAlg(readerSignature);
+ if (!coseSignAlg) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error extracting signature algorithm from COSE_Sign1"));
+ }
+ if (!hwProxy_->validateRequestMessage(sessionTranscript, itemsRequest,
+ coseSignAlg.value(), tbsSignature.value())) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "readerMessage is not signed by top-level certificate"));
+ }
}
}
- // Here's where we would validate the passed-in |authToken| to assure ourselves
- // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
- //
- // However this involves calculating the MAC. However this requires access
- // to the key needed to a pre-shared key which we don't have...
- //
+ // Feed remaining access control profiles...
+ for (const SecureAccessControlProfile& profile : remainingAcps) {
+ optional<bool> res = hwProxy_->validateAccessControlProfile(
+ profile.id, profile.readerCertificate.encodedCertificate,
+ profile.userAuthenticationRequired, profile.timeoutMillis, profile.secureUserId,
+ profile.mac);
+ if (!res) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error validating access control profile"));
+ }
+ if (res.value()) {
+ accessControlProfileMask |= (1 << profile.id);
+ }
+ }
+ // TODO: move this check to the TA
+#if 1
// To prevent replay-attacks, we check that the public part of the ephemeral
// key we previously created, is present in the DeviceEngagement part of
// SessionTranscript as a COSE_Key, in uncompressed form.
@@ -364,6 +365,7 @@
"SessionTranscript (make sure leading zeroes are not used)"));
}
}
+#endif
// itemsRequest: If non-empty, contains request data that may be signed by the
// reader. The content can be defined in the way appropriate for the
@@ -463,30 +465,6 @@
}
}
- // Validate all the access control profiles in the requestData.
- bool haveAuthToken = (authToken.timestamp.milliSeconds != int64_t(0));
- for (const auto& profile : accessControlProfiles) {
- if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
- LOG(ERROR) << "Error checking MAC for profile";
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_INVALID_DATA,
- "Error checking MAC for profile"));
- }
- int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
- if (profile.userAuthenticationRequired) {
- if (!haveAuthToken ||
- !checkUserAuthentication(profile, verificationToken_, authToken, authChallenge_)) {
- accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
- }
- } else if (profile.readerCertificate.encodedCertificate.size() > 0) {
- if (!readerCertificateChain ||
- !checkReaderAuthentication(profile, readerCertificateChain.value())) {
- accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
- }
- }
- profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
- }
-
deviceNameSpacesMap_ = cppbor::Map();
currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
@@ -496,8 +474,36 @@
itemsRequest_ = itemsRequest;
signingKeyBlob_ = signingKeyBlob;
- // Finally, calculate the size of DeviceNameSpaces. We need to know it ahead of time.
- expectedDeviceNameSpacesSize_ = calcDeviceNameSpacesSize();
+ // calculate the size of DeviceNameSpaces. We need to know it ahead of time.
+ calcDeviceNameSpacesSize(accessControlProfileMask);
+
+ // Count the number of non-empty namespaces
+ size_t numNamespacesWithValues = 0;
+ for (size_t n = 0; n < expectedNumEntriesPerNamespace_.size(); n++) {
+ if (expectedNumEntriesPerNamespace_[n] > 0) {
+ numNamespacesWithValues += 1;
+ }
+ }
+
+ // Finally, pass info so the HMAC key can be derived and the TA can start
+ // creating the DeviceNameSpaces CBOR...
+ if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 && signingKeyBlob.size() > 0) {
+ // We expect the reader ephemeral public key to be same size and curve
+ // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
+ // won't work. So its length should be 65 bytes and it should be
+ // starting with 0x04.
+ if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Reader public key is not in expected format"));
+ }
+ vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1, readerPublicKey_.end());
+ if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob, docType_,
+ numNamespacesWithValues, expectedDeviceNameSpacesSize_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
+ }
+ }
numStartRetrievalCalls_ += 1;
return ndk::ScopedAStatus::ok();
@@ -520,7 +526,7 @@
return 1 + cborNumBytesForLength(value.size()) + value.size();
}
-size_t IdentityCredential::calcDeviceNameSpacesSize() {
+void IdentityCredential::calcDeviceNameSpacesSize(uint32_t accessControlProfileMask) {
/*
* This is how DeviceNameSpaces is defined:
*
@@ -539,7 +545,7 @@
* encoded.
*/
size_t ret = 0;
- size_t numNamespacesWithValues = 0;
+ vector<unsigned int> numEntriesPerNamespace;
for (const RequestNamespace& rns : requestNamespaces_) {
vector<RequestDataItem> itemsToInclude;
@@ -562,13 +568,9 @@
//
bool authorized = false;
for (auto id : rdi.accessControlProfileIds) {
- auto it = profileIdToAccessCheckResult_.find(id);
- if (it != profileIdToAccessCheckResult_.end()) {
- int accessControlForProfile = it->second;
- if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
- authorized = true;
- break;
- }
+ if (accessControlProfileMask & (1 << id)) {
+ authorized = true;
+ break;
}
}
if (!authorized) {
@@ -578,7 +580,10 @@
itemsToInclude.push_back(rdi);
}
- // If no entries are to be in the namespace, we don't include it...
+ numEntriesPerNamespace.push_back(itemsToInclude.size());
+
+ // If no entries are to be in the namespace, we don't include it in
+ // the CBOR...
if (itemsToInclude.size() == 0) {
continue;
}
@@ -597,15 +602,14 @@
// that.
ret += item.size;
}
-
- numNamespacesWithValues++;
}
- // Now that we now the nunber of namespaces with values, we know how many
+ // Now that we know the number of namespaces with values, we know how many
// bytes the DeviceNamespaces map in the beginning is going to take up.
- ret += 1 + cborNumBytesForLength(numNamespacesWithValues);
+ ret += 1 + cborNumBytesForLength(numEntriesPerNamespace.size());
- return ret;
+ expectedDeviceNameSpacesSize_ = ret;
+ expectedNumEntriesPerNamespace_ = numEntriesPerNamespace;
}
ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
@@ -626,9 +630,11 @@
"No more name spaces left to go through"));
}
+ bool newNamespace;
if (currentNameSpace_ == "") {
// First call.
currentNameSpace_ = nameSpace;
+ newNamespace = true;
}
if (nameSpace == currentNameSpace_) {
@@ -655,6 +661,7 @@
requestCountsRemaining_.erase(requestCountsRemaining_.begin());
currentNameSpace_ = nameSpace;
+ newNamespace = true;
}
// It's permissible to have an empty itemsRequest... but if non-empty you can
@@ -674,35 +681,52 @@
}
}
- // Enforce access control.
- //
- // Access is granted if at least one of the profiles grants access.
- //
- // If an item is configured without any profiles, access is denied.
- //
- int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
- for (auto id : accessControlProfileIds) {
- auto search = profileIdToAccessCheckResult_.find(id);
- if (search == profileIdToAccessCheckResult_.end()) {
+ unsigned int newNamespaceNumEntries = 0;
+ if (newNamespace) {
+ if (expectedNumEntriesPerNamespace_.size() == 0) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_INVALID_DATA,
- "Requested entry with unvalidated profile id"));
+ "No more populated name spaces left to go through"));
}
- int accessControlForProfile = search->second;
- if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
- accessControl = IIdentityCredentialStore::STATUS_OK;
- break;
- }
- accessControl = accessControlForProfile;
- }
- if (accessControl != IIdentityCredentialStore::STATUS_OK) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- int(accessControl), "Access control check failed"));
+ newNamespaceNumEntries = expectedNumEntriesPerNamespace_[0];
+ expectedNumEntriesPerNamespace_.erase(expectedNumEntriesPerNamespace_.begin());
}
- entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+ // Access control is enforced in the secure hardware.
+ //
+ // ... except for STATUS_NOT_IN_REQUEST_MESSAGE, that's handled above (TODO:
+ // consolidate).
+ //
+ AccessCheckResult res = hwProxy_->startRetrieveEntryValue(
+ nameSpace, name, newNamespaceNumEntries, entrySize, accessControlProfileIds);
+ switch (res) {
+ case AccessCheckResult::kOk:
+ /* Do nothing. */
+ break;
+ case AccessCheckResult::kFailed:
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Access control check failed (failed)"));
+ break;
+ case AccessCheckResult::kNoAccessControlProfiles:
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES,
+ "Access control check failed (no access control profiles)"));
+ break;
+ case AccessCheckResult::kUserAuthenticationFailed:
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED,
+ "Access control check failed (user auth)"));
+ break;
+ case AccessCheckResult::kReaderAuthenticationFailed:
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED,
+ "Access control check failed (reader auth)"));
+ break;
+ }
currentName_ = name;
+ currentAccessControlProfileIds_ = accessControlProfileIds;
entryRemainingBytes_ = entrySize;
entryValue_.resize(0);
@@ -711,8 +735,8 @@
ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<uint8_t>& encryptedContent,
vector<uint8_t>* outContent) {
- optional<vector<uint8_t>> content =
- support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
+ optional<vector<uint8_t>> content = hwProxy_->retrieveEntryValue(
+ encryptedContent, currentNameSpace_, currentName_, currentAccessControlProfileIds_);
if (!content) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
@@ -777,28 +801,14 @@
optional<vector<uint8_t>> mac;
if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
readerPublicKey_.size() > 0) {
- vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> signingKey =
- support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
- if (!signingKey) {
+ optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval();
+ if (!digestToBeMaced || digestToBeMaced.value().size() != 32) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_INVALID_DATA,
- "Error decrypting signingKeyBlob"));
+ "Error generating digestToBeMaced"));
}
-
- vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
- optional<vector<uint8_t>> eMacKey =
- support::calcEMacKey(signingKey.value(), readerPublicKey_, sessionTranscriptBytes);
- if (!eMacKey) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error calculating EMacKey"));
- }
- mac = support::calcMac(sessionTranscript_, docType_, encodedDeviceNameSpaces,
- eMacKey.value());
- if (!mac) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
- }
+ // Now construct COSE_Mac0 from the returned MAC...
+ mac = support::coseMacWithDigest(digestToBeMaced.value(), {} /* data */);
}
*outMac = mac.value_or(vector<uint8_t>({}));
@@ -808,56 +818,18 @@
ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
- string serialDecimal = "1";
- string issuer = "Android Identity Credential Key";
- string subject = "Android Identity Credential Authentication Key";
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
- optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
- if (!signingKeyPKCS8) {
+ time_t now = time(NULL);
+ optional<pair<vector<uint8_t>, vector<uint8_t>>> pair =
+ hwProxy_->generateSigningKeyPair(docType_, now);
+ if (!pair) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
}
- optional<vector<uint8_t>> signingPublicKey =
- support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
- if (!signingPublicKey) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED,
- "Error getting public part of signingKey"));
- }
-
- optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
- if (!signingKey) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED,
- "Error getting private part of signingKey"));
- }
-
- optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
- signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!certificate) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
- }
-
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
- }
- vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
- storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
- if (!encryptedSigningKey) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
- }
- *outSigningKeyBlob = encryptedSigningKey.value();
*outSigningKeyCertificate = Certificate();
- outSigningKeyCertificate->encodedCertificate = certificate.value();
+ outSigningKeyCertificate->encodedCertificate = pair->first;
+
+ *outSigningKeyBlob = pair->second;
return ndk::ScopedAStatus::ok();
}
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/common/IdentityCredential.h
similarity index 88%
rename from identity/aidl/default/IdentityCredential.h
rename to identity/aidl/default/common/IdentityCredential.h
index a8a6409..2281821 100644
--- a/identity/aidl/default/IdentityCredential.h
+++ b/identity/aidl/default/common/IdentityCredential.h
@@ -29,10 +29,15 @@
#include <cppbor/cppbor.h>
+#include "IdentityCredentialStore.h"
+#include "SecureHardwareProxy.h"
+
namespace aidl::android::hardware::identity {
using ::aidl::android::hardware::keymaster::HardwareAuthToken;
using ::aidl::android::hardware::keymaster::VerificationToken;
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwarePresentationProxy;
using ::std::map;
using ::std::set;
using ::std::string;
@@ -40,10 +45,11 @@
class IdentityCredential : public BnIdentityCredential {
public:
- IdentityCredential(const vector<uint8_t>& credentialData)
- : credentialData_(credentialData),
+ IdentityCredential(sp<SecureHardwarePresentationProxy> hwProxy,
+ const vector<uint8_t>& credentialData)
+ : hwProxy_(hwProxy),
+ credentialData_(credentialData),
numStartRetrievalCalls_(0),
- authChallenge_(0),
expectedDeviceNameSpacesSize_(0) {}
// Parses and decrypts credentialData_, return a status code from
@@ -75,14 +81,13 @@
private:
// Set by constructor
+ sp<SecureHardwarePresentationProxy> hwProxy_;
vector<uint8_t> credentialData_;
int numStartRetrievalCalls_;
// Set by initialize()
string docType_;
bool testCredential_;
- vector<uint8_t> storageKey_;
- vector<uint8_t> credentialPrivKey_;
// Set by createEphemeralKeyPair()
vector<uint8_t> ephemeralPublicKey_;
@@ -90,9 +95,6 @@
// Set by setReaderEphemeralPublicKey()
vector<uint8_t> readerPublicKey_;
- // Set by createAuthChallenge()
- uint64_t authChallenge_;
-
// Set by setRequestedNamespaces()
vector<RequestNamespace> requestNamespaces_;
@@ -100,7 +102,6 @@
VerificationToken verificationToken_;
// Set at startRetrieval() time.
- map<int32_t, int> profileIdToAccessCheckResult_;
vector<uint8_t> signingKeyBlob_;
vector<uint8_t> sessionTranscript_;
vector<uint8_t> itemsRequest_;
@@ -111,15 +112,16 @@
// Calculated at startRetrieval() time.
size_t expectedDeviceNameSpacesSize_;
+ vector<unsigned int> expectedNumEntriesPerNamespace_;
// Set at startRetrieveEntryValue() time.
string currentNameSpace_;
string currentName_;
+ vector<int32_t> currentAccessControlProfileIds_;
size_t entryRemainingBytes_;
vector<uint8_t> entryValue_;
- vector<uint8_t> entryAdditionalData_;
- size_t calcDeviceNameSpacesSize();
+ void calcDeviceNameSpacesSize(uint32_t accessControlProfileMask);
};
} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredentialStore.cpp b/identity/aidl/default/common/IdentityCredentialStore.cpp
similarity index 90%
rename from identity/aidl/default/IdentityCredentialStore.cpp
rename to identity/aidl/default/common/IdentityCredentialStore.cpp
index 30dc6f3..13f91aa 100644
--- a/identity/aidl/default/IdentityCredentialStore.cpp
+++ b/identity/aidl/default/common/IdentityCredentialStore.cpp
@@ -39,8 +39,9 @@
ndk::ScopedAStatus IdentityCredentialStore::createCredential(
const string& docType, bool testCredential,
shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+ sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
shared_ptr<WritableIdentityCredential> wc =
- ndk::SharedRefBase::make<WritableIdentityCredential>(docType, testCredential);
+ ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType, testCredential);
if (!wc->initialize()) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED,
@@ -60,8 +61,9 @@
"Unsupported cipher suite"));
}
+ sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
shared_ptr<IdentityCredential> credential =
- ndk::SharedRefBase::make<IdentityCredential>(credentialData);
+ ndk::SharedRefBase::make<IdentityCredential>(hwProxy, credentialData);
auto ret = credential->initialize();
if (ret != IIdentityCredentialStore::STATUS_OK) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
diff --git a/identity/aidl/default/IdentityCredentialStore.h b/identity/aidl/default/common/IdentityCredentialStore.h
similarity index 85%
rename from identity/aidl/default/IdentityCredentialStore.h
rename to identity/aidl/default/common/IdentityCredentialStore.h
index 4f3a421..d35e632 100644
--- a/identity/aidl/default/IdentityCredentialStore.h
+++ b/identity/aidl/default/common/IdentityCredentialStore.h
@@ -19,15 +19,20 @@
#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
+#include "SecureHardwareProxy.h"
+
namespace aidl::android::hardware::identity {
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwareProxyFactory;
using ::std::shared_ptr;
using ::std::string;
using ::std::vector;
class IdentityCredentialStore : public BnIdentityCredentialStore {
public:
- IdentityCredentialStore() {}
+ IdentityCredentialStore(sp<SecureHardwareProxyFactory> hwProxyFactory)
+ : hwProxyFactory_(hwProxyFactory) {}
// The GCM chunk size used by this implementation is 64 KiB.
static constexpr size_t kGcmChunkSize = 64 * 1024;
@@ -41,6 +46,9 @@
ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<uint8_t>& credentialData,
shared_ptr<IIdentityCredential>* outCredential) override;
+
+ private:
+ sp<SecureHardwareProxyFactory> hwProxyFactory_;
};
} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/common/SecureHardwareProxy.h b/identity/aidl/default/common/SecureHardwareProxy.h
new file mode 100644
index 0000000..b89ad87
--- /dev/null
+++ b/identity/aidl/default/common/SecureHardwareProxy.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
+#define ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
+
+#include <utils/RefBase.h>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::identity {
+
+using ::android::RefBase;
+using ::std::optional;
+using ::std::pair;
+using ::std::string;
+using ::std::vector;
+
+// These classes are used to communicate with Secure Hardware. They mimic the
+// API in libEmbeddedIC 1:1 (except for using C++ types) as each call is intended
+// to be forwarded to the Secure Hardware.
+//
+// Instances are instantiated when a provisioning or presentation session
+// starts. When the session is complete, the shutdown() method is called.
+//
+
+// Forward declare.
+//
+class SecureHardwareProvisioningProxy;
+class SecureHardwarePresentationProxy;
+
+// This is a class used to create proxies.
+//
+class SecureHardwareProxyFactory : public RefBase {
+ public:
+ SecureHardwareProxyFactory() {}
+ virtual ~SecureHardwareProxyFactory() {}
+
+ virtual sp<SecureHardwareProvisioningProxy> createProvisioningProxy() = 0;
+ virtual sp<SecureHardwarePresentationProxy> createPresentationProxy() = 0;
+};
+
+// The proxy used for provisioning.
+//
+class SecureHardwareProvisioningProxy : public RefBase {
+ public:
+ SecureHardwareProvisioningProxy() {}
+ virtual ~SecureHardwareProvisioningProxy() {}
+
+ virtual bool initialize(bool testCredential) = 0;
+
+ // Returns public key certificate chain with attestation.
+ //
+ // This must return an entire certificate chain and its implementation must
+ // be coordinated with the implementation of eicOpsCreateCredentialKey() on
+ // the TA side (which may return just a single certificate or the entire
+ // chain).
+ virtual optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
+ const vector<uint8_t>& applicationId) = 0;
+
+ virtual bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts,
+ const string& docType,
+ size_t expectedProofOfProvisioningSize) = 0;
+
+ // Returns MAC (28 bytes).
+ virtual optional<vector<uint8_t>> addAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+ uint64_t timeoutMillis, uint64_t secureUserId) = 0;
+
+ virtual bool beginAddEntry(const vector<int>& accessControlProfileIds, const string& nameSpace,
+ const string& name, uint64_t entrySize) = 0;
+
+ // Returns encryptedContent.
+ virtual optional<vector<uint8_t>> addEntryValue(const vector<int>& accessControlProfileIds,
+ const string& nameSpace, const string& name,
+ const vector<uint8_t>& content) = 0;
+
+ // Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+ virtual optional<vector<uint8_t>> finishAddingEntries() = 0;
+
+ // Returns encryptedCredentialKeys (80 bytes).
+ virtual optional<vector<uint8_t>> finishGetCredentialData(const string& docType) = 0;
+
+ virtual bool shutdown() = 0;
+};
+
+enum AccessCheckResult {
+ kOk,
+ kFailed,
+ kNoAccessControlProfiles,
+ kUserAuthenticationFailed,
+ kReaderAuthenticationFailed,
+};
+
+// The proxy used for presentation.
+//
+class SecureHardwarePresentationProxy : public RefBase {
+ public:
+ SecureHardwarePresentationProxy() {}
+ virtual ~SecureHardwarePresentationProxy() {}
+
+ virtual bool initialize(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) = 0;
+
+ // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+ virtual optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType,
+ time_t now) = 0;
+
+ // Returns private key
+ virtual optional<vector<uint8_t>> createEphemeralKeyPair() = 0;
+
+ virtual optional<uint64_t> createAuthChallenge() = 0;
+
+ virtual bool startRetrieveEntries() = 0;
+
+ virtual bool setAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+ int hardwareAuthenticatorType, uint64_t timeStamp,
+ const vector<uint8_t>& mac, uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimestamp,
+ int verificationTokenSecurityLevel,
+ const vector<uint8_t>& verificationTokenMac) = 0;
+
+ virtual bool pushReaderCert(const vector<uint8_t>& certX509) = 0;
+
+ virtual optional<bool> validateAccessControlProfile(int id,
+ const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired,
+ int timeoutMillis, uint64_t secureUserId,
+ const vector<uint8_t>& mac) = 0;
+
+ virtual bool validateRequestMessage(const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& requestMessage, int coseSignAlg,
+ const vector<uint8_t>& readerSignatureOfToBeSigned) = 0;
+
+ virtual bool calcMacKey(const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& readerEphemeralPublicKey,
+ const vector<uint8_t>& signingKeyBlob, const string& docType,
+ unsigned int numNamespacesWithValues,
+ size_t expectedProofOfProvisioningSize) = 0;
+
+ virtual AccessCheckResult startRetrieveEntryValue(
+ const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+ int32_t entrySize, const vector<int32_t>& accessControlProfileIds) = 0;
+
+ virtual optional<vector<uint8_t>> retrieveEntryValue(
+ const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+ const vector<int32_t>& accessControlProfileIds) = 0;
+
+ virtual optional<vector<uint8_t>> finishRetrieval();
+
+ virtual optional<vector<uint8_t>> deleteCredential(const string& docType,
+ size_t proofOfDeletionCborSize) = 0;
+
+ virtual bool shutdown() = 0;
+};
+
+} // namespace android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/common/WritableIdentityCredential.cpp
similarity index 70%
rename from identity/aidl/default/WritableIdentityCredential.cpp
rename to identity/aidl/default/common/WritableIdentityCredential.cpp
index 141b4de..1328f36 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/common/WritableIdentityCredential.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "WritableIdentityCredential"
#include "WritableIdentityCredential.h"
-#include "IdentityCredentialStore.h"
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
@@ -30,8 +29,8 @@
#include <utility>
#include "IdentityCredentialStore.h"
-#include "Util.h"
-#include "WritableIdentityCredential.h"
+
+#include "FakeSecureHardwareProxy.h"
namespace aidl::android::hardware::identity {
@@ -40,74 +39,55 @@
using namespace ::android::hardware::identity;
bool WritableIdentityCredential::initialize() {
- optional<vector<uint8_t>> random = support::getRandom(16);
- if (!random) {
- LOG(ERROR) << "Error creating storageKey";
+ if (!hwProxy_->initialize(testCredential_)) {
+ LOG(ERROR) << "hwProxy->initialize failed";
return false;
}
- storageKey_ = random.value();
startPersonalizationCalled_ = false;
firstEntry_ = true;
return true;
}
-// This function generates the attestation certificate using the passed in
-// |attestationApplicationId| and |attestationChallenge|. It will generate an
-// attestation certificate with current time and expires one year from now. The
-// certificate shall contain all values as specified in hal.
+WritableIdentityCredential::~WritableIdentityCredential() {}
+
ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
- const vector<uint8_t>& attestationApplicationId, //
- const vector<uint8_t>& attestationChallenge, //
- vector<Certificate>* outCertificateChain) {
- if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
+ const vector<uint8_t>& attestationApplicationId,
+ const vector<uint8_t>& attestationChallenge, vector<Certificate>* outCertificateChain) {
+ if (getAttestationCertificateAlreadyCalled_) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED,
"Error attestation certificate previously generated"));
}
+ getAttestationCertificateAlreadyCalled_ = true;
+
if (attestationChallenge.empty()) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
}
- vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
- vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
-
- optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
- support::createEcKeyPairAndAttestation(challenge, appId, testCredential_);
- if (!keyAttestationPair) {
- LOG(ERROR) << "Error creating credentialKey and attestation";
+ optional<vector<uint8_t>> certChain =
+ hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
+ if (!certChain) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED,
- "Error creating credentialKey and attestation"));
+ "Error generating attestation certificate chain"));
}
- vector<uint8_t> keyPair = keyAttestationPair.value().first;
- certificateChain_ = keyAttestationPair.value().second;
-
- optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
- if (!pubKey) {
+ optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certChain.value());
+ if (!certs) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED,
- "Error getting public part of credentialKey"));
+ "Error splitting chain into separate certificates"));
}
- credentialPubKey_ = pubKey.value();
- optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
- if (!privKey) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED,
- "Error getting private part of credentialKey"));
- }
- credentialPrivKey_ = privKey.value();
-
- // convert from vector<vector<uint8_t>>> to vector<Certificate>*
*outCertificateChain = vector<Certificate>();
- for (const vector<uint8_t>& cert : certificateChain_) {
+ for (const vector<uint8_t>& cert : certs.value()) {
Certificate c = Certificate();
c.encodedCertificate = cert;
outCertificateChain->push_back(std::move(c));
}
+
return ndk::ScopedAStatus::ok();
}
@@ -123,8 +103,8 @@
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
}
-
startPersonalizationCalled_ = true;
+
numAccessControlProfileRemaining_ = accessControlProfileCount;
remainingEntryCounts_ = entryCounts;
entryNameSpace_ = "";
@@ -133,6 +113,12 @@
signedDataNamespaces_ = cppbor::Map();
signedDataCurrentNamespace_ = cppbor::Array();
+ if (!hwProxy_->startPersonalization(accessControlProfileCount, entryCounts, docType_,
+ expectedProofOfProvisioningSize_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "eicStartPersonalization"));
+ }
+
return ndk::ScopedAStatus::ok();
}
@@ -140,8 +126,6 @@
int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
int64_t timeoutMillis, int64_t secureUserId,
SecureAccessControlProfile* outSecureAccessControlProfile) {
- SecureAccessControlProfile profile;
-
if (numAccessControlProfileRemaining_ == 0) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_INVALID_DATA,
@@ -169,25 +153,21 @@
"userAuthenticationRequired is false but timeout is non-zero"));
}
- // If |userAuthenticationRequired| is true, then |secureUserId| must be non-zero.
- if (userAuthenticationRequired && secureUserId == 0) {
+ optional<vector<uint8_t>> mac = hwProxy_->addAccessControlProfile(
+ id, readerCertificate.encodedCertificate, userAuthenticationRequired, timeoutMillis,
+ secureUserId);
+ if (!mac) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_INVALID_DATA,
- "userAuthenticationRequired is true but secureUserId is zero"));
+ IIdentityCredentialStore::STATUS_FAILED, "eicAddAccessControlProfile"));
}
+ SecureAccessControlProfile profile;
profile.id = id;
profile.readerCertificate = readerCertificate;
profile.userAuthenticationRequired = userAuthenticationRequired;
profile.timeoutMillis = timeoutMillis;
profile.secureUserId = secureUserId;
- optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
- if (!mac) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
- }
profile.mac = mac.value();
-
cppbor::Map profileMap;
profileMap.add("id", profile.id);
if (profile.readerCertificate.encodedCertificate.size() > 0) {
@@ -261,14 +241,18 @@
remainingEntryCounts_[0] -= 1;
}
- entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
entryRemainingBytes_ = entrySize;
entryNameSpace_ = nameSpace;
entryName_ = name;
entryAccessControlProfileIds_ = accessControlProfileIds;
entryBytes_.resize(0);
// LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
+
+ if (!hwProxy_->beginAddEntry(accessControlProfileIds, nameSpace, name, entrySize)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "eicBeginAddEntry"));
+ }
+
return ndk::ScopedAStatus::ok();
}
@@ -297,16 +281,11 @@
}
}
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
- }
- optional<vector<uint8_t>> encryptedContent =
- support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
+ optional<vector<uint8_t>> encryptedContent = hwProxy_->addEntryValue(
+ entryAccessControlProfileIds_, entryNameSpace_, entryName_, content);
if (!encryptedContent) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
+ IIdentityCredentialStore::STATUS_FAILED, "eicAddEntryValue"));
}
if (entryRemainingBytes_ == 0) {
@@ -332,50 +311,6 @@
return ndk::ScopedAStatus::ok();
}
-// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
-// |credentialPrivKey|.
-static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
- const vector<uint8_t>& credentialPrivKey,
- vector<uint8_t>& credentialKeys) {
- if (storageKey.size() != 16) {
- LOG(ERROR) << "Size of storageKey is not 16";
- return false;
- }
-
- cppbor::Array array;
- array.add(cppbor::Bstr(storageKey));
- array.add(cppbor::Bstr(credentialPrivKey));
- credentialKeys = array.encode();
- return true;
-}
-
-// Writes CBOR-encoded structure to |credentialData| containing |docType|,
-// |testCredential| and |credentialKeys|. The latter element will be stored in
-// encrypted form, using |hardwareBoundKey| as the encryption key.
-bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
- bool testCredential, const vector<uint8_t>& credentialKeys,
- vector<uint8_t>& credentialData) {
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- LOG(ERROR) << "Error getting random";
- return false;
- }
- vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
- optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
- hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
- if (!credentialBlob) {
- LOG(ERROR) << "Error encrypting CredentialKeys blob";
- return false;
- }
-
- cppbor::Array array;
- array.add(docType);
- array.add(testCredential);
- array.add(cppbor::Bstr(credentialBlob.value()));
- credentialData = array.encode();
- return true;
-}
-
ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
if (numAccessControlProfileRemaining_ != 0) {
@@ -411,31 +346,37 @@
.c_str()));
}
- optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
- encodedCbor, // payload
- {}, // additionalData
- {}); // certificateChain
+ optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->finishAddingEntries();
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "eicFinishAddingEntries"));
+ }
+
+ optional<vector<uint8_t>> signature =
+ support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+ encodedCbor, // data
+ {}); // certificateChain
if (!signature) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
}
- vector<uint8_t> credentialKeys;
- if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
+ optional<vector<uint8_t>> encryptedCredentialKeys = hwProxy_->finishGetCredentialData(docType_);
+ if (!encryptedCredentialKeys) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error generating encrypted CredentialKeys"));
}
-
- vector<uint8_t> credentialData;
- if (!generateCredentialData(
- testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
- docType_, testCredential_, credentialKeys, credentialData)) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
- }
+ cppbor::Array array;
+ array.add(docType_);
+ array.add(testCredential_);
+ array.add(encryptedCredentialKeys.value());
+ vector<uint8_t> credentialData = array.encode();
*outCredentialData = credentialData;
*outProofOfProvisioningSignature = signature.value();
+ hwProxy_->shutdown();
+
return ndk::ScopedAStatus::ok();
}
diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/common/WritableIdentityCredential.h
similarity index 85%
rename from identity/aidl/default/WritableIdentityCredential.h
rename to identity/aidl/default/common/WritableIdentityCredential.h
index 5645852..c6f0628 100644
--- a/identity/aidl/default/WritableIdentityCredential.h
+++ b/identity/aidl/default/common/WritableIdentityCredential.h
@@ -23,16 +23,24 @@
#include <cppbor.h>
#include <set>
+#include "IdentityCredentialStore.h"
+#include "SecureHardwareProxy.h"
+
namespace aidl::android::hardware::identity {
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwareProvisioningProxy;
using ::std::set;
using ::std::string;
using ::std::vector;
class WritableIdentityCredential : public BnWritableIdentityCredential {
public:
- WritableIdentityCredential(const string& docType, bool testCredential)
- : docType_(docType), testCredential_(testCredential) {}
+ WritableIdentityCredential(sp<SecureHardwareProvisioningProxy> hwProxy, const string& docType,
+ bool testCredential)
+ : hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
+
+ ~WritableIdentityCredential();
// Creates the Credential Key. Returns false on failure. Must be called
// right after construction.
@@ -57,7 +65,6 @@
ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds,
const string& nameSpace, const string& name,
int32_t entrySize) override;
-
ndk::ScopedAStatus addEntryValue(const vector<uint8_t>& content,
vector<uint8_t>* outEncryptedContent) override;
@@ -66,18 +73,17 @@
vector<uint8_t>* outProofOfProvisioningSignature) override;
private:
+ // Set by constructor.
+ sp<SecureHardwareProvisioningProxy> hwProxy_;
string docType_;
bool testCredential_;
// This is set in initialize().
- vector<uint8_t> storageKey_;
bool startPersonalizationCalled_;
bool firstEntry_;
- // These are set in getAttestationCertificate().
- vector<uint8_t> credentialPrivKey_;
- vector<uint8_t> credentialPubKey_;
- vector<vector<uint8_t>> certificateChain_;
+ // This is set in getAttestationCertificate().
+ bool getAttestationCertificateAlreadyCalled_ = false;
// These fields are initialized during startPersonalization()
size_t numAccessControlProfileRemaining_;
@@ -92,7 +98,6 @@
// These fields are initialized during beginAddEntry()
size_t entryRemainingBytes_;
- vector<uint8_t> entryAdditionalData_;
string entryNameSpace_;
string entryName_;
vector<int32_t> entryAccessControlProfileIds_;
diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml
index a47d354..37d5b81 100644
--- a/identity/aidl/default/identity-default.xml
+++ b/identity/aidl/default/identity-default.xml
@@ -1,6 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.identity</name>
+ <version>2</version>
<interface>
<name>IIdentityCredentialStore</name>
<instance>default</instance>
diff --git a/identity/aidl/default/libeic/EicCbor.c b/identity/aidl/default/libeic/EicCbor.c
new file mode 100644
index 0000000..ec049b1
--- /dev/null
+++ b/identity/aidl/default/libeic/EicCbor.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright 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.
+ */
+
+#include "EicCbor.h"
+
+void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) {
+ cbor->size = 0;
+ cbor->bufferSize = bufferSize;
+ cbor->buffer = buffer;
+ cbor->digestType = EIC_CBOR_DIGEST_TYPE_SHA256;
+ eicOpsSha256Init(&cbor->digester.sha256);
+}
+
+void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
+ const uint8_t* hmacKey, size_t hmacKeySize) {
+ cbor->size = 0;
+ cbor->bufferSize = bufferSize;
+ cbor->buffer = buffer;
+ cbor->digestType = EIC_CBOR_DIGEST_TYPE_HMAC_SHA256;
+ eicOpsHmacSha256Init(&cbor->digester.hmacSha256, hmacKey, hmacKeySize);
+}
+
+void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+ switch (cbor->digestType) {
+ case EIC_CBOR_DIGEST_TYPE_SHA256:
+ eicOpsSha256Final(&cbor->digester.sha256, digest);
+ break;
+ case EIC_CBOR_DIGEST_TYPE_HMAC_SHA256:
+ eicOpsHmacSha256Final(&cbor->digester.hmacSha256, digest);
+ break;
+ }
+}
+
+void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size) {
+ switch (cbor->digestType) {
+ case EIC_CBOR_DIGEST_TYPE_SHA256:
+ eicOpsSha256Update(&cbor->digester.sha256, data, size);
+ break;
+ case EIC_CBOR_DIGEST_TYPE_HMAC_SHA256:
+ eicOpsHmacSha256Update(&cbor->digester.hmacSha256, data, size);
+ break;
+ }
+
+ if (cbor->size >= cbor->bufferSize) {
+ cbor->size += size;
+ return;
+ }
+
+ size_t numBytesLeft = cbor->bufferSize - cbor->size;
+ size_t numBytesToCopy = size;
+ if (numBytesToCopy > numBytesLeft) {
+ numBytesToCopy = numBytesLeft;
+ }
+ eicMemCpy(cbor->buffer + cbor->size, data, numBytesToCopy);
+
+ cbor->size += size;
+}
+
+size_t eicCborAdditionalLengthBytesFor(size_t size) {
+ if (size < 24) {
+ return 0;
+ } else if (size <= 0xff) {
+ return 1;
+ } else if (size <= 0xffff) {
+ return 2;
+ } else if (size <= 0xffffffff) {
+ return 4;
+ }
+ return 8;
+}
+
+void eicCborBegin(EicCbor* cbor, int majorType, size_t size) {
+ uint8_t data[9];
+
+ if (size < 24) {
+ data[0] = (majorType << 5) | size;
+ eicCborAppend(cbor, data, 1);
+ } else if (size <= 0xff) {
+ data[0] = (majorType << 5) | 24;
+ data[1] = size;
+ eicCborAppend(cbor, data, 2);
+ } else if (size <= 0xffff) {
+ data[0] = (majorType << 5) | 25;
+ data[1] = size >> 8;
+ data[2] = size & 0xff;
+ eicCborAppend(cbor, data, 3);
+ } else if (size <= 0xffffffff) {
+ data[0] = (majorType << 5) | 26;
+ data[1] = (size >> 24) & 0xff;
+ data[2] = (size >> 16) & 0xff;
+ data[3] = (size >> 8) & 0xff;
+ data[4] = size & 0xff;
+ eicCborAppend(cbor, data, 5);
+ } else {
+ data[0] = (majorType << 5) | 24;
+ data[1] = (((uint64_t)size) >> 56) & 0xff;
+ data[2] = (((uint64_t)size) >> 48) & 0xff;
+ data[3] = (((uint64_t)size) >> 40) & 0xff;
+ data[4] = (((uint64_t)size) >> 32) & 0xff;
+ data[5] = (((uint64_t)size) >> 24) & 0xff;
+ data[6] = (((uint64_t)size) >> 16) & 0xff;
+ data[7] = (((uint64_t)size) >> 8) & 0xff;
+ data[8] = ((uint64_t)size) & 0xff;
+ eicCborAppend(cbor, data, 9);
+ }
+}
+
+void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data, size_t dataSize) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dataSize);
+ eicCborAppend(cbor, data, dataSize);
+}
+
+void eicCborAppendString(EicCbor* cbor, const char* str) {
+ size_t length = eicStrLen(str);
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_STRING, length);
+ eicCborAppend(cbor, (const uint8_t*)str, length);
+}
+
+void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_SIMPLE, simpleValue);
+}
+
+void eicCborAppendBool(EicCbor* cbor, bool value) {
+ uint8_t simpleValue = value ? EIC_CBOR_SIMPLE_VALUE_TRUE : EIC_CBOR_SIMPLE_VALUE_FALSE;
+ eicCborAppendSimple(cbor, simpleValue);
+}
+
+void eicCborAppendSemantic(EicCbor* cbor, uint64_t value) {
+ size_t encoded = value;
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_SEMANTIC, encoded);
+}
+
+void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value) {
+ size_t encoded = value;
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_UNSIGNED, encoded);
+}
+
+void eicCborAppendNumber(EicCbor* cbor, int64_t value) {
+ if (value < 0) {
+ size_t encoded = -1 - value;
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_NEGATIVE, encoded);
+ } else {
+ eicCborAppendUnsigned(cbor, value);
+ }
+}
+
+void eicCborAppendArray(EicCbor* cbor, size_t numElements) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, numElements);
+}
+
+void eicCborAppendMap(EicCbor* cbor, size_t numPairs) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_MAP, numPairs);
+}
+
+bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* readerCertificate,
+ size_t readerCertificateSize, bool userAuthenticationRequired,
+ uint64_t timeoutMillis, uint64_t secureUserId) {
+ size_t numPairs = 1;
+ if (readerCertificateSize > 0) {
+ numPairs += 1;
+ }
+ if (userAuthenticationRequired) {
+ numPairs += 2;
+ if (secureUserId > 0) {
+ numPairs += 1;
+ }
+ }
+ eicCborAppendMap(cborBuilder, numPairs);
+ eicCborAppendString(cborBuilder, "id");
+ eicCborAppendUnsigned(cborBuilder, id);
+ if (readerCertificateSize > 0) {
+ eicCborAppendString(cborBuilder, "readerCertificate");
+ eicCborAppendByteString(cborBuilder, readerCertificate, readerCertificateSize);
+ }
+ if (userAuthenticationRequired) {
+ eicCborAppendString(cborBuilder, "userAuthenticationRequired");
+ eicCborAppendBool(cborBuilder, userAuthenticationRequired);
+ eicCborAppendString(cborBuilder, "timeoutMillis");
+ eicCborAppendUnsigned(cborBuilder, timeoutMillis);
+ if (secureUserId > 0) {
+ eicCborAppendString(cborBuilder, "secureUserId");
+ eicCborAppendUnsigned(cborBuilder, secureUserId);
+ }
+ }
+
+ if (cborBuilder->size > cborBuilder->bufferSize) {
+ eicDebug("Buffer for ACP CBOR is too small (%zd) - need %zd bytes", cborBuilder->bufferSize,
+ cborBuilder->size);
+ return false;
+ }
+
+ return true;
+}
+
+bool eicCborCalcEntryAdditionalData(const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, const char* nameSpace,
+ const char* name, uint8_t* cborBuffer, size_t cborBufferSize,
+ size_t* outAdditionalDataCborSize,
+ uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE]) {
+ EicCbor cborBuilder;
+
+ eicCborInit(&cborBuilder, cborBuffer, cborBufferSize);
+ eicCborAppendMap(&cborBuilder, 3);
+ eicCborAppendString(&cborBuilder, "Namespace");
+ eicCborAppendString(&cborBuilder, nameSpace);
+ eicCborAppendString(&cborBuilder, "Name");
+ eicCborAppendString(&cborBuilder, name);
+ eicCborAppendString(&cborBuilder, "AccessControlProfileIds");
+ eicCborAppendArray(&cborBuilder, numAccessControlProfileIds);
+ for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+ eicCborAppendNumber(&cborBuilder, accessControlProfileIds[n]);
+ }
+ if (cborBuilder.size > cborBufferSize) {
+ eicDebug("Not enough space for additionalData - buffer is only %zd bytes, content is %zd",
+ cborBufferSize, cborBuilder.size);
+ return false;
+ }
+ if (outAdditionalDataCborSize != NULL) {
+ *outAdditionalDataCborSize = cborBuilder.size;
+ }
+ eicCborFinal(&cborBuilder, additionalDataSha256);
+ return true;
+}
diff --git a/identity/aidl/default/libeic/EicCbor.h b/identity/aidl/default/libeic/EicCbor.h
new file mode 100644
index 0000000..4686b38
--- /dev/null
+++ b/identity/aidl/default/libeic/EicCbor.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 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.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicOps.h"
+
+typedef enum {
+ EIC_CBOR_DIGEST_TYPE_SHA256,
+ EIC_CBOR_DIGEST_TYPE_HMAC_SHA256,
+} EicCborDigestType;
+
+/* EicCbor is a utility class to build CBOR data structures and calculate
+ * digests on the fly.
+ */
+typedef struct {
+ // Contains the size of the built CBOR, even if it exceeds bufferSize (will
+ // never write to buffer beyond bufferSize though)
+ size_t size;
+
+ // The size of the buffer. Is zero if no data is recorded in which case
+ // only digesting is performed.
+ size_t bufferSize;
+
+ // Whether we're producing a SHA-256 or HMAC-SHA256 digest.
+ EicCborDigestType digestType;
+
+ // The SHA-256 digester object.
+ union {
+ EicSha256Ctx sha256;
+ EicHmacSha256Ctx hmacSha256;
+ } digester;
+
+ // The buffer used for building up CBOR or NULL if bufferSize is 0.
+ uint8_t* buffer;
+} EicCbor;
+
+/* Initializes an EicCbor.
+ *
+ * The given buffer will be used, up to bufferSize.
+ *
+ * If bufferSize is 0, buffer may be NULL.
+ */
+void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize);
+
+/* Like eicCborInit() but uses HMAC-SHA256 instead of SHA-256.
+ */
+void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
+ const uint8_t* hmacKey, size_t hmacKeySize);
+
+/* Finishes building CBOR and returns the digest. */
+void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+/* Appends CBOR data to the EicCbor. */
+void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size);
+
+#define EIC_CBOR_MAJOR_TYPE_UNSIGNED 0
+#define EIC_CBOR_MAJOR_TYPE_NEGATIVE 1
+#define EIC_CBOR_MAJOR_TYPE_BYTE_STRING 2
+#define EIC_CBOR_MAJOR_TYPE_STRING 3
+#define EIC_CBOR_MAJOR_TYPE_ARRAY 4
+#define EIC_CBOR_MAJOR_TYPE_MAP 5
+#define EIC_CBOR_MAJOR_TYPE_SEMANTIC 6
+#define EIC_CBOR_MAJOR_TYPE_SIMPLE 7
+
+#define EIC_CBOR_SIMPLE_VALUE_FALSE 20
+#define EIC_CBOR_SIMPLE_VALUE_TRUE 21
+
+#define EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR 24
+
+/* Begins a new CBOR value. */
+void eicCborBegin(EicCbor* cbor, int majorType, size_t size);
+
+/* Appends a bytestring. */
+void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data, size_t dataSize);
+
+/* Appends a NUL-terminated UTF-8 string. */
+void eicCborAppendString(EicCbor* cbor, const char* str);
+
+/* Appends a simple value. */
+void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue);
+
+/* Appends a boolean. */
+void eicCborAppendBool(EicCbor* cbor, bool value);
+
+/* Appends a semantic */
+void eicCborAppendSemantic(EicCbor* cbor, uint64_t value);
+
+/* Appends an unsigned number. */
+void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value);
+
+/* Appends a number. */
+void eicCborAppendNumber(EicCbor* cbor, int64_t value);
+
+/* Starts appending an array.
+ *
+ * After this numElements CBOR elements must follow.
+ */
+void eicCborAppendArray(EicCbor* cbor, size_t numElements);
+
+/* Starts appending a map.
+ *
+ * After this numPairs pairs of CBOR elements must follow.
+ */
+void eicCborAppendMap(EicCbor* cbor, size_t numPairs);
+
+/* Calculates how many bytes are needed to store a size. */
+size_t eicCborAdditionalLengthBytesFor(size_t size);
+
+bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* readerCertificate,
+ size_t readerCertificateSize, bool userAuthenticationRequired,
+ uint64_t timeoutMillis, uint64_t secureUserId);
+
+bool eicCborCalcEntryAdditionalData(const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, const char* nameSpace,
+ const char* name, uint8_t* cborBuffer, size_t cborBufferSize,
+ size_t* outAdditionalDataCborSize,
+ uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE]);
+
+// The maximum size of an encoded Secure Access Control Profile that we
+// support. Since the SACP may contain a reader certificate chain these can get
+// pretty big.
+//
+// Currently we allocate space on the stack for this structure which is why we
+// have a maximum size. We can get rid of the maximum size by incrementally
+// building/verifying the SACP. TODO: actually do this.
+//
+#define EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE 512
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
diff --git a/identity/aidl/default/libeic/EicOps.h b/identity/aidl/default/libeic/EicOps.h
new file mode 100644
index 0000000..da4dabf
--- /dev/null
+++ b/identity/aidl/default/libeic/EicOps.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 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.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Uncomment or define if debug messages are needed.
+//
+//#define EIC_DEBUG
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The following defines must be set to something appropriate
+//
+// EIC_SHA256_CONTEXT_SIZE - the size of EicSha256Ctx
+// EIC_HMAC_SHA256_CONTEXT_SIZE - the size of EicHmacSha256Ctx
+//
+// For example, if EicSha256Ctx is implemented using BoringSSL this would be defined
+// as sizeof(SHA256_CTX).
+//
+// We expect the implementation to provide a header file with the name
+// EicOpsImpl.h to do all this.
+//
+#include "EicOpsImpl.h"
+
+#define EIC_SHA256_DIGEST_SIZE 32
+
+// The size of a P-256 private key.
+//
+#define EIC_P256_PRIV_KEY_SIZE 32
+
+// The size of a P-256 public key in uncompressed form.
+//
+// The public key is stored in uncompressed form, first the X coordinate, then
+// the Y coordinate.
+//
+#define EIC_P256_PUB_KEY_SIZE 64
+
+// Size of one of the coordinates in a curve-point.
+//
+#define EIC_P256_COORDINATE_SIZE 32
+
+// The size of an ECSDA signature using P-256.
+//
+// The R and S values are stored here, first R then S.
+//
+#define EIC_ECDSA_P256_SIGNATURE_SIZE 64
+
+#define EIC_AES_128_KEY_SIZE 16
+
+// The following are definitions of implementation functions the
+// underlying platform must provide.
+//
+
+struct EicSha256Ctx {
+ uint8_t reserved[EIC_SHA256_CONTEXT_SIZE];
+};
+typedef struct EicSha256Ctx EicSha256Ctx;
+
+struct EicHmacSha256Ctx {
+ uint8_t reserved[EIC_HMAC_SHA256_CONTEXT_SIZE];
+};
+typedef struct EicHmacSha256Ctx EicHmacSha256Ctx;
+
+#ifdef EIC_DEBUG
+// Debug macro. Don't include a new-line in message.
+//
+#define eicDebug(...) \
+ do { \
+ eicPrint("%s:%d: ", __FILE__, __LINE__); \
+ eicPrint(__VA_ARGS__); \
+ eicPrint("\n"); \
+ } while (0)
+#else
+#define eicDebug(...) \
+ do { \
+ } while (0)
+#endif
+
+// Prints message which should include new-line character. Can be no-op.
+//
+// Don't use this from code, use eicDebug() instead.
+//
+#ifdef EIC_DEBUG
+void eicPrint(const char* format, ...);
+#else
+inline void eicPrint(const char*, ...) {}
+#endif
+
+// Dumps data as pretty-printed hex. Can be no-op.
+//
+#ifdef EIC_DEBUG
+void eicHexdump(const char* message, const uint8_t* data, size_t dataSize);
+#else
+inline void eicHexdump(const char*, const uint8_t*, size_t) {}
+#endif
+
+// Pretty-prints encoded CBOR. Can be no-op.
+//
+// If a byte-string is larger than |maxBStrSize| its contents will not be
+// printed, instead the value of the form "<bstr size=1099016
+// sha1=ef549cca331f73dfae2090e6a37c04c23f84b07b>" will be printed. Pass zero
+// for |maxBStrSize| to disable this.
+//
+#ifdef EIC_DEBUG
+void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize, size_t maxBStrSize);
+#else
+inline void eicCborPrettyPrint(const uint8_t*, size_t, size_t) {}
+#endif
+
+// Memory setting, see memset(3).
+void* eicMemSet(void* s, int c, size_t n);
+
+// Memory copying, see memcpy(3).
+void* eicMemCpy(void* dest, const void* src, size_t n);
+
+// String length, see strlen(3).
+size_t eicStrLen(const char* s);
+
+// Memory compare, see CRYPTO_memcmp(3SSL)
+//
+// It takes an amount of time dependent on len, but independent of the contents of the
+// memory regions pointed to by s1 and s2.
+//
+int eicCryptoMemCmp(const void* s1, const void* s2, size_t n);
+
+// Random number generation.
+bool eicOpsRandom(uint8_t* buf, size_t numBytes);
+
+// If |testCredential| is true, returns the 128-bit AES Hardware-Bound Key (16 bytes).
+//
+// Otherwise returns all zeroes (16 bytes).
+//
+const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential);
+
+// Encrypts |data| with |key| and |additionalAuthenticatedData| using |nonce|,
+// returns the resulting (nonce || ciphertext || tag) in |encryptedData| which
+// must be of size |dataSize| + 28.
+bool eicOpsEncryptAes128Gcm(
+ const uint8_t* key, // Must be 16 bytes
+ const uint8_t* nonce, // Must be 12 bytes
+ const uint8_t* data, // May be NULL if size is 0
+ size_t dataSize,
+ const uint8_t* additionalAuthenticationData, // May be NULL if size is 0
+ size_t additionalAuthenticationDataSize, uint8_t* encryptedData);
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext in |data| must be of size |encryptedDataSize| - 28.
+//
+// The format of |encryptedData| must be as specified in the
+// encryptAes128Gcm() function.
+bool eicOpsDecryptAes128Gcm(const uint8_t* key, // Must be 16 bytes
+ const uint8_t* encryptedData, size_t encryptedDataSize,
+ const uint8_t* additionalAuthenticationData,
+ size_t additionalAuthenticationDataSize, uint8_t* data);
+
+// Creates an EC key using the P-256 curve. The private key is written to
+// |privateKey|. The public key is written to |publicKey|.
+//
+bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]);
+
+// Generates CredentialKey plus an attestation certificate.
+//
+// The attestation certificate will be signed by the attestation keys the secure
+// area has been provisioned with. The given |challenge| and |applicationId|
+// will be used as will |testCredential|.
+//
+// The generated certificate will be in X.509 format and returned in |cert|
+// and |certSize| must be set to the size of this array and this function will
+// set it to the size of the certification chain on successfully return.
+//
+// This may return either a single certificate or an entire certificate
+// chain. If it returns only a single certificate, the implementation of
+// SecureHardwareProvisioningProxy::createCredentialKey() should amend the
+// remainder of the certificate chain on the HAL side.
+//
+bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge,
+ size_t challengeSize, const uint8_t* applicationId,
+ size_t applicationIdSize, bool testCredential, uint8_t* cert,
+ size_t* certSize); // inout
+
+// Generate an X.509 certificate for the key identified by |publicKey| which
+// must be of the form returned by eicOpsCreateEcKey().
+//
+// The certificate will be signed by the key identified by |signingKey| which
+// must be of the form returned by eicOpsCreateEcKey().
+//
+bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
+ const char* issuerName, const char* subjectName, time_t validityNotBefore,
+ time_t validityNotAfter, uint8_t* cert,
+ size_t* certSize); // inout
+
+// Uses |privateKey| to create an ECDSA signature of some data (the SHA-256 must
+// be given by |digestOfData|). Returns the signature in |signature|.
+//
+bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE],
+ uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+// Performs Elliptic Curve Diffie-Helman.
+//
+bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]);
+
+// Performs HKDF.
+//
+bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize, const uint8_t* salt,
+ size_t saltSize, const uint8_t* info, size_t infoSize, uint8_t* output,
+ size_t outputSize);
+
+// SHA-256 functions.
+void eicOpsSha256Init(EicSha256Ctx* ctx);
+void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len);
+void eicOpsSha256Final(EicSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+// HMAC SHA-256 functions.
+void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key, size_t keySize);
+void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data, size_t len);
+void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+// Extracts the public key in the given X.509 certificate.
+//
+// If the key is not an EC key, this function fails.
+//
+// Otherwise the public key is stored in uncompressed form in |publicKey| which
+// size should be set in |publicKeySize|. On successful return |publicKeySize|
+// is set to the length of the key. If there is not enough space, the function
+// fails.
+//
+// (The public key returned is not necessarily a P-256 key, even if it is note
+// that its size is not EIC_P256_PUBLIC_KEY_SIZE because of the leading 0x04.)
+//
+bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize, uint8_t* publicKey,
+ size_t* publicKeySize);
+
+// Checks that the X.509 certificate given by |x509Cert| is signed by the public
+// key given by |publicKey| which must be an EC key in uncompressed form (e.g.
+// same formatt as returned by eicOpsX509GetPublicKey()).
+//
+bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert, size_t x509CertSize,
+ const uint8_t* publicKey, size_t publicKeySize);
+
+// Checks that |signature| is a signature of some data (given by |digest|),
+// signed by the public key given by |publicKey|.
+//
+// The key must be an EC key in uncompressed form (e.g. same format as returned
+// by eicOpsX509GetPublicKey()).
+//
+// The format of the signature is the same encoding as the 'signature' field of
+// COSE_Sign1 - that is, it's the R and S integers both with the same length as
+// the key-size.
+//
+// The size of digest must match the size of the key.
+//
+bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize,
+ const uint8_t* signature, size_t signatureSize,
+ const uint8_t* publicKey, size_t publicKeySize);
+
+// Validates that the passed in data constitutes a valid auth- and verification tokens.
+//
+bool eicOpsValidateAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+ int hardwareAuthenticatorType, uint64_t timeStamp, const uint8_t* mac,
+ size_t macSize, uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimeStamp,
+ int verificationTokenSecurityLevel,
+ const uint8_t* verificationTokenMac, size_t verificationTokenMacSize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
diff --git a/identity/aidl/default/libeic/EicPresentation.c b/identity/aidl/default/libeic/EicPresentation.c
new file mode 100644
index 0000000..d3f5556
--- /dev/null
+++ b/identity/aidl/default/libeic/EicPresentation.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright 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.
+ */
+
+#include "EicPresentation.h"
+
+#include <inttypes.h>
+
+bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
+ const uint8_t encryptedCredentialKeys[80]) {
+ uint8_t credentialKeys[52];
+
+ eicMemSet(ctx, '\0', sizeof(EicPresentation));
+
+ if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
+ 80,
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
+ eicDebug("Error decrypting CredentialKeys");
+ return false;
+ }
+
+ // It's supposed to look like this;
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr ; credentialPrivKey, the private key for credentialKey
+ // ]
+ //
+ // where storageKey is 16 bytes and credentialPrivateKey is 32 bytes.
+ //
+ // So the first two bytes will be 0x82 0x50 indicating resp. an array of two elements
+ // and a bstr of 16 elements. Sixteen bytes later (offset 18 and 19) there will be
+ // a bstr of 32 bytes. It's encoded as two bytes 0x58 and 0x20.
+ //
+ if (credentialKeys[0] != 0x82 || credentialKeys[1] != 0x50 || credentialKeys[18] != 0x58 ||
+ credentialKeys[19] != 0x20) {
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
+ eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
+ ctx->testCredential = testCredential;
+ return true;
+}
+
+bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
+ uint8_t* publicKeyCert, size_t* publicKeyCertSize,
+ uint8_t signingKeyBlob[60]) {
+ uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
+ uint8_t signingKeyPub[EIC_P256_PUB_KEY_SIZE];
+
+ if (!eicOpsCreateEcKey(signingKeyPriv, signingKeyPub)) {
+ eicDebug("Error creating signing key");
+ return false;
+ }
+
+ const int secondsInOneYear = 365 * 24 * 60 * 60;
+ time_t validityNotBefore = now;
+ time_t validityNotAfter = now + secondsInOneYear; // One year from now.
+ if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
+ "Android Identity Credential Key", // issuer CN
+ "Android Identity Credential Authentication Key", // subject CN
+ validityNotBefore, validityNotAfter, publicKeyCert, publicKeyCertSize)) {
+ eicDebug("Error creating certificate for signing key");
+ return false;
+ }
+
+ uint8_t nonce[12];
+ if (!eicOpsRandom(nonce, 12)) {
+ eicDebug("Error getting random");
+ return false;
+ }
+ if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, signingKeyPriv, sizeof(signingKeyPriv),
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, eicStrLen(docType), signingKeyBlob)) {
+ eicDebug("Error encrypting signing key");
+ return false;
+ }
+
+ return true;
+}
+
+bool eicPresentationCreateEphemeralKeyPair(EicPresentation* ctx,
+ uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]) {
+ uint8_t ephemeralPublicKey[EIC_P256_PUB_KEY_SIZE];
+ if (!eicOpsCreateEcKey(ctx->ephemeralPrivateKey, ephemeralPublicKey)) {
+ eicDebug("Error creating ephemeral key");
+ return false;
+ }
+ eicMemCpy(ephemeralPrivateKey, ctx->ephemeralPrivateKey, EIC_P256_PRIV_KEY_SIZE);
+ return true;
+}
+
+bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChallenge) {
+ do {
+ if (!eicOpsRandom((uint8_t*)&(ctx->authChallenge), sizeof(uint64_t))) {
+ eicDebug("Failed generating random challenge");
+ return false;
+ }
+ } while (ctx->authChallenge == 0);
+ eicDebug("Created auth challenge %" PRIu64, ctx->authChallenge);
+ *authChallenge = ctx->authChallenge;
+ return true;
+}
+
+// From "COSE Algorithms" registry
+//
+#define COSE_ALG_ECDSA_256 -7
+
+bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* sessionTranscript,
+ size_t sessionTranscriptSize,
+ const uint8_t* requestMessage, size_t requestMessageSize,
+ int coseSignAlg,
+ const uint8_t* readerSignatureOfToBeSigned,
+ size_t readerSignatureOfToBeSignedSize) {
+ if (ctx->readerPublicKeySize == 0) {
+ eicDebug("No public key for reader");
+ return false;
+ }
+
+ // Right now we only support ECDSA with SHA-256 (e.g. ES256).
+ //
+ if (coseSignAlg != COSE_ALG_ECDSA_256) {
+ eicDebug(
+ "COSE Signature algorithm for reader signature is %d, "
+ "only ECDSA with SHA-256 is supported right now",
+ coseSignAlg);
+ return false;
+ }
+
+ // What we're going to verify is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ // So we're going to build that CBOR...
+ //
+ EicCbor cbor;
+ eicCborInit(&cbor, NULL, 0);
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendString(&cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> coseSignAlg (e.g. -7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // External_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time... the CBOR to be written is
+ //
+ // ReaderAuthentication = [
+ // "ReaderAuthentication",
+ // SessionTranscript,
+ // ItemsRequestBytes
+ // ]
+ //
+ // ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+ //
+ // ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+ //
+ // which is easily calculated below
+ //
+ size_t calculatedSize = 0;
+ calculatedSize += 1; // Array of size 3
+ calculatedSize += 1; // "ReaderAuthentication" less than 24 bytes
+ calculatedSize += sizeof("ReaderAuthentication") - 1; // Don't include trailing NUL
+ calculatedSize += sessionTranscriptSize; // Already CBOR encoded
+ calculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+ calculatedSize += 1 + eicCborAdditionalLengthBytesFor(requestMessageSize);
+ calculatedSize += requestMessageSize;
+
+ // However note that we're authenticating ReaderAuthenticationBytes which
+ // is a tagged bstr of the bytes of ReaderAuthentication. So need to get
+ // that in front.
+ size_t rabCalculatedSize = 0;
+ rabCalculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+ rabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
+ rabCalculatedSize += calculatedSize;
+
+ // Begin the bytestring for ReaderAuthenticationBytes;
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, rabCalculatedSize);
+
+ eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+ // Begins the bytestring for ReaderAuthentication;
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
+
+ // And now that we know the size, let's fill it in...
+ //
+ size_t payloadOffset = cbor.size;
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, 3);
+ eicCborAppendString(&cbor, "ReaderAuthentication");
+ eicCborAppend(&cbor, sessionTranscript, sessionTranscriptSize);
+ eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, requestMessageSize);
+ eicCborAppend(&cbor, requestMessage, requestMessageSize);
+
+ if (cbor.size != payloadOffset + calculatedSize) {
+ eicDebug("CBOR size is %zd but we expected %zd", cbor.size, payloadOffset + calculatedSize);
+ return false;
+ }
+ uint8_t toBeSignedDigest[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, toBeSignedDigest);
+
+ if (!eicOpsEcDsaVerifyWithPublicKey(
+ toBeSignedDigest, EIC_SHA256_DIGEST_SIZE, readerSignatureOfToBeSigned,
+ readerSignatureOfToBeSignedSize, ctx->readerPublicKey, ctx->readerPublicKeySize)) {
+ eicDebug("Request message is not signed by public key");
+ return false;
+ }
+ ctx->requestMessageValidated = true;
+ return true;
+}
+
+// Validates the next certificate in the reader certificate chain.
+bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509,
+ size_t certX509Size) {
+ // If we had a previous certificate, use its public key to validate this certificate.
+ if (ctx->readerPublicKeySize > 0) {
+ if (!eicOpsX509CertSignedByPublicKey(certX509, certX509Size, ctx->readerPublicKey,
+ ctx->readerPublicKeySize)) {
+ eicDebug("Certificate is not signed by public key in the previous certificate");
+ return false;
+ }
+ }
+
+ // Store the key of this certificate, this is used to validate the next certificate
+ // and also ACPs with certificates that use the same public key...
+ ctx->readerPublicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
+ if (!eicOpsX509GetPublicKey(certX509, certX509Size, ctx->readerPublicKey,
+ &ctx->readerPublicKeySize)) {
+ eicDebug("Error extracting public key from certificate");
+ return false;
+ }
+ if (ctx->readerPublicKeySize == 0) {
+ eicDebug("Zero-length public key in certificate");
+ return false;
+ }
+
+ return true;
+}
+
+bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
+ uint64_t authenticatorId, int hardwareAuthenticatorType,
+ uint64_t timeStamp, const uint8_t* mac, size_t macSize,
+ uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimestamp,
+ int verificationTokenSecurityLevel,
+ const uint8_t* verificationTokenMac,
+ size_t verificationTokenMacSize) {
+ if (!eicOpsValidateAuthToken(
+ challenge, secureUserId, authenticatorId, hardwareAuthenticatorType, timeStamp, mac,
+ macSize, verificationTokenChallenge, verificationTokenTimestamp,
+ verificationTokenSecurityLevel, verificationTokenMac, verificationTokenMacSize)) {
+ return false;
+ }
+ ctx->authTokenChallenge = challenge;
+ ctx->authTokenSecureUserId = secureUserId;
+ ctx->authTokenTimestamp = timeStamp;
+ ctx->verificationTokenTimestamp = verificationTokenTimestamp;
+ return true;
+}
+
+static bool checkUserAuth(EicPresentation* ctx, bool userAuthenticationRequired, int timeoutMillis,
+ uint64_t secureUserId) {
+ if (!userAuthenticationRequired) {
+ return true;
+ }
+
+ if (secureUserId != ctx->authTokenSecureUserId) {
+ eicDebug("secureUserId in profile differs from userId in authToken");
+ return false;
+ }
+
+ if (timeoutMillis == 0) {
+ if (ctx->authTokenChallenge == 0) {
+ eicDebug("No challenge in authToken");
+ return false;
+ }
+
+ // If we didn't create a challenge, too bad but user auth with
+ // timeoutMillis set to 0 needs it.
+ if (ctx->authChallenge == 0) {
+ eicDebug("No challenge was created for this session");
+ return false;
+ }
+ if (ctx->authTokenChallenge != ctx->authChallenge) {
+ eicDebug("Challenge in authToken (%" PRIu64
+ ") doesn't match the challenge "
+ "that was created (%" PRIu64 ") for this session",
+ ctx->authTokenChallenge, ctx->authChallenge);
+ return false;
+ }
+ }
+
+ uint64_t now = ctx->verificationTokenTimestamp;
+ if (ctx->authTokenTimestamp > now) {
+ eicDebug("Timestamp in authToken is in the future");
+ return false;
+ }
+
+ if (timeoutMillis > 0) {
+ if (now > ctx->authTokenTimestamp + timeoutMillis) {
+ eicDebug("Deadline for authToken is in the past");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool checkReaderAuth(EicPresentation* ctx, const uint8_t* readerCertificate,
+ size_t readerCertificateSize) {
+ uint8_t publicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
+ size_t publicKeySize;
+
+ if (readerCertificateSize == 0) {
+ return true;
+ }
+
+ // Remember in this case certificate equality is done by comparing public
+ // keys, not bitwise comparison of the certificates.
+ //
+ publicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
+ if (!eicOpsX509GetPublicKey(readerCertificate, readerCertificateSize, publicKey,
+ &publicKeySize)) {
+ eicDebug("Error extracting public key from certificate");
+ return false;
+ }
+ if (publicKeySize == 0) {
+ eicDebug("Zero-length public key in certificate");
+ return false;
+ }
+
+ if ((ctx->readerPublicKeySize != publicKeySize) ||
+ (eicCryptoMemCmp(ctx->readerPublicKey, publicKey, ctx->readerPublicKeySize) != 0)) {
+ return false;
+ }
+ return true;
+}
+
+// Note: This function returns false _only_ if an error occurred check for access, _not_
+// whether access is granted. Whether access is granted is returned in |accessGranted|.
+//
+bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id,
+ const uint8_t* readerCertificate,
+ size_t readerCertificateSize,
+ bool userAuthenticationRequired, int timeoutMillis,
+ uint64_t secureUserId, const uint8_t mac[28],
+ bool* accessGranted) {
+ *accessGranted = false;
+
+ if (id < 0 || id >= 32) {
+ eicDebug("id value of %d is out of allowed range [0, 32[", id);
+ return false;
+ }
+
+ // Validate the MAC
+ uint8_t cborBuffer[EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE];
+ EicCbor cborBuilder;
+ eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+ if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+ userAuthenticationRequired, timeoutMillis, secureUserId)) {
+ return false;
+ }
+ if (!eicOpsDecryptAes128Gcm(ctx->storageKey, mac, 28, cborBuilder.buffer, cborBuilder.size,
+ NULL)) {
+ eicDebug("MAC for AccessControlProfile doesn't match");
+ return false;
+ }
+
+ bool passedUserAuth =
+ checkUserAuth(ctx, userAuthenticationRequired, timeoutMillis, secureUserId);
+ bool passedReaderAuth = checkReaderAuth(ctx, readerCertificate, readerCertificateSize);
+
+ ctx->accessControlProfileMaskValidated |= (1 << id);
+ if (readerCertificateSize > 0) {
+ ctx->accessControlProfileMaskUsesReaderAuth |= (1 << id);
+ }
+ if (!passedReaderAuth) {
+ ctx->accessControlProfileMaskFailedReaderAuth |= (1 << id);
+ }
+ if (!passedUserAuth) {
+ ctx->accessControlProfileMaskFailedUserAuth |= (1 << id);
+ }
+
+ if (passedUserAuth && passedReaderAuth) {
+ *accessGranted = true;
+ eicDebug("Access granted for id %d", id);
+ }
+ return true;
+}
+
+bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTranscript,
+ size_t sessionTranscriptSize,
+ const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t signingKeyBlob[60], const char* docType,
+ unsigned int numNamespacesWithValues,
+ size_t expectedDeviceNamespacesSize) {
+ uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
+ if (!eicOpsDecryptAes128Gcm(ctx->storageKey, signingKeyBlob, 60, (const uint8_t*)docType,
+ eicStrLen(docType), signingKeyPriv)) {
+ eicDebug("Error decrypting signingKeyBlob");
+ return false;
+ }
+
+ uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE];
+ if (!eicOpsEcdh(readerEphemeralPublicKey, signingKeyPriv, sharedSecret)) {
+ eicDebug("ECDH failed");
+ return false;
+ }
+
+ EicCbor cbor;
+ eicCborInit(&cbor, NULL, 0);
+ eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+ eicCborAppendByteString(&cbor, sessionTranscript, sessionTranscriptSize);
+ uint8_t salt[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, salt);
+
+ const uint8_t info[7] = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+ uint8_t derivedKey[32];
+ if (!eicOpsHkdf(sharedSecret, EIC_P256_COORDINATE_SIZE, salt, sizeof(salt), info, sizeof(info),
+ derivedKey, sizeof(derivedKey))) {
+ eicDebug("HKDF failed");
+ return false;
+ }
+
+ eicCborInitHmacSha256(&ctx->cbor, NULL, 0, derivedKey, sizeof(derivedKey));
+ ctx->buildCbor = true;
+
+ // What we're going to calculate the HMAC-SHA256 is the COSE ToBeMaced
+ // structure which looks like the following:
+ //
+ // MAC_structure = [
+ // context : "MAC" / "MAC0",
+ // protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&ctx->cbor, 4);
+ eicCborAppendString(&ctx->cbor, "MAC0");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_HMAC_256_256 (5). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x05};
+ eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time... the CBOR to be written is
+ //
+ // DeviceAuthentication = [
+ // "DeviceAuthentication",
+ // SessionTranscript,
+ // DocType, ; DocType as used in Documents structure in OfflineResponse
+ // DeviceNameSpacesBytes
+ // ]
+ //
+ // DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+ //
+ // DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+ //
+ // which is easily calculated below
+ //
+ size_t calculatedSize = 0;
+ calculatedSize += 1; // Array of size 4
+ calculatedSize += 1; // "DeviceAuthentication" less than 24 bytes
+ calculatedSize += sizeof("DeviceAuthentication") - 1; // Don't include trailing NUL
+ calculatedSize += sessionTranscriptSize; // Already CBOR encoded
+ size_t docTypeLen = eicStrLen(docType);
+ calculatedSize += 1 + eicCborAdditionalLengthBytesFor(docTypeLen) + docTypeLen;
+ calculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+ calculatedSize += 1 + eicCborAdditionalLengthBytesFor(expectedDeviceNamespacesSize);
+ calculatedSize += expectedDeviceNamespacesSize;
+
+ // However note that we're authenticating DeviceAuthenticationBytes which
+ // is a tagged bstr of the bytes of DeviceAuthentication. So need to get
+ // that in front.
+ size_t dabCalculatedSize = 0;
+ dabCalculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+ dabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
+ dabCalculatedSize += calculatedSize;
+
+ // Begin the bytestring for DeviceAuthenticationBytes;
+ eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dabCalculatedSize);
+
+ eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+ // Begins the bytestring for DeviceAuthentication;
+ eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
+
+ eicCborAppendArray(&ctx->cbor, 4);
+ eicCborAppendString(&ctx->cbor, "DeviceAuthentication");
+ eicCborAppend(&ctx->cbor, sessionTranscript, sessionTranscriptSize);
+ eicCborAppendString(&ctx->cbor, docType);
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+ eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedDeviceNamespacesSize);
+ ctx->expectedCborSizeAtEnd = expectedDeviceNamespacesSize + ctx->cbor.size;
+
+ eicCborAppendMap(&ctx->cbor, numNamespacesWithValues);
+ return true;
+}
+
+bool eicPresentationStartRetrieveEntries(EicPresentation* ctx) {
+ // HAL may use this object multiple times to retrieve data so need to reset various
+ // state objects here.
+ ctx->requestMessageValidated = false;
+ ctx->buildCbor = false;
+ ctx->accessControlProfileMaskValidated = 0;
+ ctx->accessControlProfileMaskUsesReaderAuth = 0;
+ ctx->accessControlProfileMaskFailedReaderAuth = 0;
+ ctx->accessControlProfileMaskFailedUserAuth = 0;
+ ctx->readerPublicKeySize = 0;
+ return true;
+}
+
+EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
+ EicPresentation* ctx, const char* nameSpace, const char* name,
+ unsigned int newNamespaceNumEntries, int32_t /* entrySize */,
+ const int* accessControlProfileIds, size_t numAccessControlProfileIds,
+ uint8_t* scratchSpace, size_t scratchSpaceSize) {
+ uint8_t* additionalDataCbor = scratchSpace;
+ const size_t additionalDataCborBufSize = scratchSpaceSize;
+ size_t additionalDataCborSize;
+
+ if (newNamespaceNumEntries > 0) {
+ eicCborAppendString(&ctx->cbor, nameSpace);
+ eicCborAppendMap(&ctx->cbor, newNamespaceNumEntries);
+ }
+
+ // We'll need to calc and store a digest of additionalData to check that it's the same
+ // additionalData being passed in for every eicPresentationRetrieveEntryValue() call...
+ if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+ nameSpace, name, additionalDataCbor,
+ additionalDataCborBufSize, &additionalDataCborSize,
+ ctx->additionalDataSha256)) {
+ return EIC_ACCESS_CHECK_RESULT_FAILED;
+ }
+
+ if (numAccessControlProfileIds == 0) {
+ return EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES;
+ }
+
+ // Access is granted if at least one of the profiles grants access.
+ //
+ // If an item is configured without any profiles, access is denied.
+ //
+ EicAccessCheckResult result = EIC_ACCESS_CHECK_RESULT_FAILED;
+ for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+ int id = accessControlProfileIds[n];
+ uint32_t idBitMask = (1 << id);
+
+ // If the access control profile wasn't validated, this is an error and we
+ // fail immediately.
+ bool validated = ((ctx->accessControlProfileMaskValidated & idBitMask) != 0);
+ if (!validated) {
+ eicDebug("No ACP for profile id %d", id);
+ return EIC_ACCESS_CHECK_RESULT_FAILED;
+ }
+
+ // Otherwise, we _did_ validate the profile. If none of the checks
+ // failed, we're done
+ bool failedUserAuth = ((ctx->accessControlProfileMaskFailedUserAuth & idBitMask) != 0);
+ bool failedReaderAuth = ((ctx->accessControlProfileMaskFailedReaderAuth & idBitMask) != 0);
+ if (!failedUserAuth && !failedReaderAuth) {
+ result = EIC_ACCESS_CHECK_RESULT_OK;
+ break;
+ }
+ // One of the checks failed, convey which one
+ if (failedUserAuth) {
+ result = EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED;
+ } else {
+ result = EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED;
+ }
+ }
+ eicDebug("Result %d for name %s", result, name);
+
+ if (result == EIC_ACCESS_CHECK_RESULT_OK) {
+ eicCborAppendString(&ctx->cbor, name);
+ }
+ return result;
+}
+
+// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
+bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
+ size_t encryptedContentSize, uint8_t* content,
+ const char* nameSpace, const char* name,
+ const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, uint8_t* scratchSpace,
+ size_t scratchSpaceSize) {
+ uint8_t* additionalDataCbor = scratchSpace;
+ const size_t additionalDataCborBufSize = scratchSpaceSize;
+ size_t additionalDataCborSize;
+
+ uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
+ if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+ nameSpace, name, additionalDataCbor,
+ additionalDataCborBufSize, &additionalDataCborSize,
+ calculatedSha256)) {
+ return false;
+ }
+ if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
+ eicDebug("SHA-256 mismatch of additionalData");
+ return false;
+ }
+
+ if (!eicOpsDecryptAes128Gcm(ctx->storageKey, encryptedContent, encryptedContentSize,
+ additionalDataCbor, additionalDataCborSize, content)) {
+ eicDebug("Error decrypting content");
+ return false;
+ }
+
+ eicCborAppend(&ctx->cbor, content, encryptedContentSize - 28);
+
+ return true;
+}
+
+bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
+ size_t* digestToBeMacedSize) {
+ if (!ctx->buildCbor) {
+ *digestToBeMacedSize = 0;
+ return true;
+ }
+ if (*digestToBeMacedSize != 32) {
+ return false;
+ }
+
+ // This verifies that the correct expectedDeviceNamespacesSize value was
+ // passed in at eicPresentationCalcMacKey() time.
+ if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
+ eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
+ return false;
+ }
+ eicCborFinal(&ctx->cbor, digestToBeMaced);
+ return true;
+}
+
+bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
+ size_t proofOfDeletionCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ EicCbor cbor;
+
+ eicCborInit(&cbor, NULL, 0);
+
+ // What we're going to sign is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendString(&cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
+
+ // Finally, the CBOR that we're actually signing.
+ eicCborAppendArray(&cbor, 3);
+ eicCborAppendString(&cbor, "ProofOfDeletion");
+ eicCborAppendString(&cbor, docType);
+ eicCborAppendBool(&cbor, ctx->testCredential);
+
+ uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, cborSha256);
+ if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+ eicDebug("Error signing proofOfDeletion");
+ return false;
+ }
+
+ return true;
+}
diff --git a/identity/aidl/default/libeic/EicPresentation.h b/identity/aidl/default/libeic/EicPresentation.h
new file mode 100644
index 0000000..d798962
--- /dev/null
+++ b/identity/aidl/default/libeic/EicPresentation.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright 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.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicCbor.h"
+
+// The maximum size we support for public keys in reader certificates.
+#define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65
+
+typedef struct {
+ uint8_t storageKey[EIC_AES_128_KEY_SIZE];
+ uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+ uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+ // The challenge generated with eicPresentationCreateAuthChallenge()
+ uint64_t authChallenge;
+
+ // Set by eicPresentationSetAuthToken() and contains the fields
+ // from the passed in authToken and verificationToken.
+ //
+ uint64_t authTokenChallenge;
+ uint64_t authTokenSecureUserId;
+ uint64_t authTokenTimestamp;
+ uint64_t verificationTokenTimestamp;
+
+ // The public key for the reader.
+ //
+ // (During the process of pushing reader certificates, this is also used to store
+ // the public key of the previously pushed certificate.)
+ //
+ uint8_t readerPublicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
+ size_t readerPublicKeySize;
+
+ // This is set to true only if eicPresentationValidateRequestMessage() successfully
+ // validated the requestMessage.
+ //
+ // Why even record this? Because there's no requirement the HAL actually calls that
+ // function and we validate ACPs before it's called... so it's possible that a
+ // compromised HAL could trick us into marking ACPs as authorized while they in fact
+ // aren't.
+ bool requestMessageValidated;
+ bool buildCbor;
+
+ // Set to true initialized as a test credential.
+ bool testCredential;
+
+ // These are bitmasks indicating which of the possible 32 access control profiles are
+ // authorized. They are built up by eicPresentationValidateAccessControlProfile().
+ //
+ uint32_t accessControlProfileMaskValidated; // True if the profile was validated.
+ uint32_t accessControlProfileMaskUsesReaderAuth; // True if the ACP is using reader auth
+ uint32_t accessControlProfileMaskFailedReaderAuth; // True if failed reader auth
+ uint32_t accessControlProfileMaskFailedUserAuth; // True if failed user auth
+
+ // SHA-256 for AdditionalData, updated for each entry.
+ uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+
+ size_t expectedCborSizeAtEnd;
+ EicCbor cbor;
+} EicPresentation;
+
+bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
+ const uint8_t encryptedCredentialKeys[80]);
+
+bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
+ uint8_t* publicKeyCert, size_t* publicKeyCertSize,
+ uint8_t signingKeyBlob[60]);
+
+// Create an ephemeral key-pair.
+//
+// The private key is stored in |ctx->ephemeralPrivateKey| and also returned in
+// |ephemeralPrivateKey|.
+//
+bool eicPresentationCreateEphemeralKeyPair(EicPresentation* ctx,
+ uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]);
+
+// Returns a non-zero challenge in |authChallenge|.
+bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChallenge);
+
+// Starts retrieveing entries.
+//
+bool eicPresentationStartRetrieveEntries(EicPresentation* ctx);
+
+// Sets the auth-token.
+bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
+ uint64_t authenticatorId, int hardwareAuthenticatorType,
+ uint64_t timeStamp, const uint8_t* mac, size_t macSize,
+ uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimeStamp,
+ int verificationTokenSecurityLevel,
+ const uint8_t* verificationTokenMac,
+ size_t verificationTokenMacSize);
+
+// Function to push certificates in the reader certificate chain.
+//
+// This should start with the root certificate (e.g. the last in the chain) and
+// continue up the chain, ending with the certificate for the reader.
+//
+// Calls to this function should be interleaved with calls to the
+// eicPresentationValidateAccessControlProfile() function, see below.
+//
+bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509,
+ size_t certX509Size);
+
+// Checks an access control profile.
+//
+// Returns false if an error occurred while checking the profile (e.g. MAC doesn't check out).
+//
+// Returns in |accessGranted| whether access is granted.
+//
+// If |readerCertificate| is non-empty and the public key of one of those
+// certificates appear in the chain presented by the reader, this function must
+// be called after pushing that certificate using
+// eicPresentationPushReaderCert().
+//
+bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id,
+ const uint8_t* readerCertificate,
+ size_t readerCertificateSize,
+ bool userAuthenticationRequired, int timeoutMillis,
+ uint64_t secureUserId, const uint8_t mac[28],
+ bool* accessGranted);
+
+// Validates that the given requestMessage is signed by the public key in the
+// certificate last set with eicPresentationPushReaderCert().
+//
+// The format of the signature is the same encoding as the 'signature' field of
+// COSE_Sign1 - that is, it's the R and S integers both with the same length as
+// the key-size.
+//
+// Must be called after eicPresentationPushReaderCert() have been used to push
+// the final certificate. Which is the certificate of the reader itself.
+//
+bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* sessionTranscript,
+ size_t sessionTranscriptSize,
+ const uint8_t* requestMessage, size_t requestMessageSize,
+ int coseSignAlg,
+ const uint8_t* readerSignatureOfToBeSigned,
+ size_t readerSignatureOfToBeSignedSize);
+
+typedef enum {
+ // Returned if access is granted.
+ EIC_ACCESS_CHECK_RESULT_OK,
+
+ // Returned if an error occurred checking for access.
+ EIC_ACCESS_CHECK_RESULT_FAILED,
+
+ // Returned if access was denied because item is configured without any
+ // access control profiles.
+ EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES,
+
+ // Returned if access was denied because of user authentication.
+ EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED,
+
+ // Returned if access was denied because of reader authentication.
+ EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED,
+} EicAccessCheckResult;
+
+// Passes enough information to calculate the MACing key
+//
+bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTranscript,
+ size_t sessionTranscriptSize,
+ const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t signingKeyBlob[60], const char* docType,
+ unsigned int numNamespacesWithValues,
+ size_t expectedDeviceNamespacesSize);
+
+// The scratchSpace should be set to a buffer at least 512 bytes (ideally 1024
+// bytes, the bigger the better). It's done this way to avoid allocating stack
+// space.
+//
+EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
+ EicPresentation* ctx, const char* nameSpace, const char* name,
+ unsigned int newNamespaceNumEntries, int32_t entrySize, const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, uint8_t* scratchSpace, size_t scratchSpaceSize);
+
+// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
+ size_t encryptedContentSize, uint8_t* content,
+ const char* nameSpace, const char* name,
+ const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, uint8_t* scratchSpace,
+ size_t scratchSpaceSize);
+
+// Returns the HMAC-SHA256 of |ToBeMaced| as per RFC 8051 "6.3. How to Compute
+// and Verify a MAC".
+bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
+ size_t* digestToBeMacedSize);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfDeletion CBOR.
+//
+bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
+ size_t proofOfDeletionCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
diff --git a/identity/aidl/default/libeic/EicProvisioning.c b/identity/aidl/default/libeic/EicProvisioning.c
new file mode 100644
index 0000000..f16605c
--- /dev/null
+++ b/identity/aidl/default/libeic/EicProvisioning.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright 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.
+ */
+
+#include "EicProvisioning.h"
+
+bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) {
+ eicMemSet(ctx, '\0', sizeof(EicProvisioning));
+ ctx->testCredential = testCredential;
+ if (!eicOpsRandom(ctx->storageKey, EIC_AES_128_KEY_SIZE)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
+ size_t challengeSize, const uint8_t* applicationId,
+ size_t applicationIdSize, uint8_t* publicKeyCert,
+ size_t* publicKeyCertSize) {
+ if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
+ applicationId, applicationIdSize, ctx->testCredential,
+ publicKeyCert, publicKeyCertSize)) {
+ return false;
+ }
+ return true;
+}
+
+bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
+ const int* entryCounts, size_t numEntryCounts,
+ const char* docType,
+ size_t expectedProofOfProvisioningSize) {
+ if (numEntryCounts >= EIC_MAX_NUM_NAMESPACES) {
+ return false;
+ }
+ if (accessControlProfileCount >= EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS) {
+ return false;
+ }
+
+ ctx->numEntryCounts = numEntryCounts;
+ if (numEntryCounts > EIC_MAX_NUM_NAMESPACES) {
+ return false;
+ }
+ for (size_t n = 0; n < numEntryCounts; n++) {
+ if (entryCounts[n] >= 256) {
+ return false;
+ }
+ ctx->entryCounts[n] = entryCounts[n];
+ }
+ ctx->curNamespace = -1;
+ ctx->curNamespaceNumProcessed = 0;
+
+ eicCborInit(&ctx->cbor, NULL, 0);
+
+ // What we're going to sign is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&ctx->cbor, 4);
+ eicCborAppendString(&ctx->cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
+ ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
+
+ eicCborAppendArray(&ctx->cbor, 5);
+ eicCborAppendString(&ctx->cbor, "ProofOfProvisioning");
+ eicCborAppendString(&ctx->cbor, docType);
+
+ eicCborAppendArray(&ctx->cbor, accessControlProfileCount);
+
+ return true;
+}
+
+bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id,
+ const uint8_t* readerCertificate,
+ size_t readerCertificateSize,
+ bool userAuthenticationRequired, uint64_t timeoutMillis,
+ uint64_t secureUserId, uint8_t outMac[28]) {
+ uint8_t cborBuffer[EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE];
+ EicCbor cborBuilder;
+
+ eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+
+ if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+ userAuthenticationRequired, timeoutMillis, secureUserId)) {
+ return false;
+ }
+
+ // Calculate and return MAC
+ uint8_t nonce[12];
+ if (!eicOpsRandom(nonce, 12)) {
+ return false;
+ }
+ if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, NULL, 0, cborBuilder.buffer,
+ cborBuilder.size, outMac)) {
+ return false;
+ }
+
+ // The ACP CBOR in the provisioning receipt doesn't include secureUserId so build
+ // it again.
+ eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+ if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+ userAuthenticationRequired, timeoutMillis,
+ 0 /* secureUserId */)) {
+ return false;
+ }
+
+ // Append the CBOR from the local builder to the digester.
+ eicCborAppend(&ctx->cbor, cborBuilder.buffer, cborBuilder.size);
+
+ return true;
+}
+
+bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, const char* nameSpace,
+ const char* name, uint64_t entrySize, uint8_t* scratchSpace,
+ size_t scratchSpaceSize) {
+ uint8_t* additionalDataCbor = scratchSpace;
+ const size_t additionalDataCborBufSize = scratchSpaceSize;
+ size_t additionalDataCborSize;
+
+ // We'll need to calc and store a digest of additionalData to check that it's the same
+ // additionalData being passed in for every eicProvisioningAddEntryValue() call...
+ if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+ nameSpace, name, additionalDataCbor,
+ additionalDataCborBufSize, &additionalDataCborSize,
+ ctx->additionalDataSha256)) {
+ return false;
+ }
+
+ if (ctx->curNamespace == -1) {
+ ctx->curNamespace = 0;
+ ctx->curNamespaceNumProcessed = 0;
+ // Opens the main map: { * Namespace => [ + Entry ] }
+ eicCborAppendMap(&ctx->cbor, ctx->numEntryCounts);
+ eicCborAppendString(&ctx->cbor, nameSpace);
+ // Opens the per-namespace array: [ + Entry ]
+ eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
+ }
+
+ if (ctx->curNamespaceNumProcessed == ctx->entryCounts[ctx->curNamespace]) {
+ ctx->curNamespace += 1;
+ ctx->curNamespaceNumProcessed = 0;
+ eicCborAppendString(&ctx->cbor, nameSpace);
+ // Opens the per-namespace array: [ + Entry ]
+ eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
+ }
+
+ eicCborAppendMap(&ctx->cbor, 3);
+ eicCborAppendString(&ctx->cbor, "name");
+ eicCborAppendString(&ctx->cbor, name);
+
+ ctx->curEntrySize = entrySize;
+ ctx->curEntryNumBytesReceived = 0;
+
+ eicCborAppendString(&ctx->cbor, "value");
+
+ ctx->curNamespaceNumProcessed += 1;
+ return true;
+}
+
+bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, const char* nameSpace,
+ const char* name, const uint8_t* content, size_t contentSize,
+ uint8_t* outEncryptedContent, uint8_t* scratchSpace,
+ size_t scratchSpaceSize) {
+ uint8_t* additionalDataCbor = scratchSpace;
+ const size_t additionalDataCborBufSize = scratchSpaceSize;
+ size_t additionalDataCborSize;
+
+ uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
+ if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+ nameSpace, name, additionalDataCbor,
+ additionalDataCborBufSize, &additionalDataCborSize,
+ calculatedSha256)) {
+ return false;
+ }
+ if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
+ eicDebug("SHA-256 mismatch of additionalData");
+ return false;
+ }
+
+ eicCborAppend(&ctx->cbor, content, contentSize);
+
+ uint8_t nonce[12];
+ if (!eicOpsRandom(nonce, 12)) {
+ return false;
+ }
+ if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, content, contentSize, additionalDataCbor,
+ additionalDataCborSize, outEncryptedContent)) {
+ return false;
+ }
+
+ // If done with this entry, close the map
+ ctx->curEntryNumBytesReceived += contentSize;
+ if (ctx->curEntryNumBytesReceived == ctx->curEntrySize) {
+ eicCborAppendString(&ctx->cbor, "accessControlProfiles");
+ eicCborAppendArray(&ctx->cbor, numAccessControlProfileIds);
+ for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+ eicCborAppendNumber(&ctx->cbor, accessControlProfileIds[n]);
+ }
+ }
+ return true;
+}
+
+bool eicProvisioningFinishAddingEntries(
+ EicProvisioning* ctx, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+
+ eicCborAppendBool(&ctx->cbor, ctx->testCredential);
+ eicCborFinal(&ctx->cbor, cborSha256);
+
+ // This verifies that the correct expectedProofOfProvisioningSize value was
+ // passed in at eicStartPersonalization() time.
+ if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
+ eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
+ return false;
+ }
+
+ if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+ eicDebug("Error signing proofOfProvisioning");
+ return false;
+ }
+
+ return true;
+}
+
+bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
+ uint8_t encryptedCredentialKeys[80]) {
+ EicCbor cbor;
+ uint8_t cborBuf[52];
+
+ eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
+ eicCborAppendArray(&cbor, 2);
+ eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
+ eicCborAppendByteString(&cbor, ctx->credentialPrivateKey, EIC_P256_PRIV_KEY_SIZE);
+ if (cbor.size > sizeof(cborBuf)) {
+ eicDebug("Exceeded buffer size");
+ return false;
+ }
+
+ uint8_t nonce[12];
+ if (!eicOpsRandom(nonce, 12)) {
+ eicDebug("Error getting random");
+ return false;
+ }
+ if (!eicOpsEncryptAes128Gcm(
+ eicOpsGetHardwareBoundKey(ctx->testCredential), nonce, cborBuf, cbor.size,
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, eicStrLen(docType), encryptedCredentialKeys)) {
+ eicDebug("Error encrypting CredentialKeys");
+ return false;
+ }
+
+ return true;
+}
diff --git a/identity/aidl/default/libeic/EicProvisioning.h b/identity/aidl/default/libeic/EicProvisioning.h
new file mode 100644
index 0000000..836d16e
--- /dev/null
+++ b/identity/aidl/default/libeic/EicProvisioning.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 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.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicCbor.h"
+
+#define EIC_MAX_NUM_NAMESPACES 32
+#define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
+
+typedef struct {
+ // Set by eicCreateCredentialKey.
+ uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+ int numEntryCounts;
+ uint8_t entryCounts[EIC_MAX_NUM_NAMESPACES];
+
+ int curNamespace;
+ int curNamespaceNumProcessed;
+
+ size_t curEntrySize;
+ size_t curEntryNumBytesReceived;
+
+ uint8_t storageKey[EIC_AES_128_KEY_SIZE];
+
+ size_t expectedCborSizeAtEnd;
+
+ // SHA-256 for AdditionalData, updated for each entry.
+ uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+
+ EicCbor cbor;
+
+ bool testCredential;
+} EicProvisioning;
+
+bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential);
+
+bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
+ size_t challengeSize, const uint8_t* applicationId,
+ size_t applicationIdSize, uint8_t* publicKeyCert,
+ size_t* publicKeyCertSize);
+
+bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
+ const int* entryCounts, size_t numEntryCounts,
+ const char* docType,
+ size_t expectedProofOfProvisioningingSize);
+
+bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id,
+ const uint8_t* readerCertificate,
+ size_t readerCertificateSize,
+ bool userAuthenticationRequired, uint64_t timeoutMillis,
+ uint64_t secureUserId, uint8_t outMac[28]);
+
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, const char* nameSpace,
+ const char* name, uint64_t entrySize, uint8_t* scratchSpace,
+ size_t scratchSpaceSize);
+
+// The outEncryptedContent array must be contentSize + 28 bytes long.
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControlProfileIds,
+ size_t numAccessControlProfileIds, const char* nameSpace,
+ const char* name, const uint8_t* content, size_t contentSize,
+ uint8_t* outEncryptedContent, uint8_t* scratchSpace,
+ size_t scratchSpaceSize);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfProvisioninging CBOR.
+//
+bool eicProvisioningFinishAddingEntries(
+ EicProvisioning* ctx, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+//
+//
+// The |encryptedCredentialKeys| array is set to AES-GCM-ENC(HBK, R, CredentialKeys, docType)
+// where
+//
+// CredentialKeys = [
+// bstr, ; storageKey, a 128-bit AES key
+// bstr ; credentialPrivKey, the private key for credentialKey
+// ]
+//
+// Since |storageKey| is 16 bytes and |credentialPrivKey| is 32 bytes, the
+// encoded CBOR for CredentialKeys is 52 bytes and consequently
+// |encryptedCredentialKeys| will be 52 + 28 = 80 bytes.
+//
+bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
+ uint8_t encryptedCredentialKeys[80]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
diff --git a/identity/aidl/default/libeic/libeic.h b/identity/aidl/default/libeic/libeic.h
new file mode 100644
index 0000000..88abef8
--- /dev/null
+++ b/identity/aidl/default/libeic/libeic.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_LIBEIC_H
+#define ANDROID_HARDWARE_IDENTITY_LIBEIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The EIC_INSIDE_LIBEIC_H preprocessor symbol is used to enforce
+ * library users to include only this file. All public interfaces, and
+ * only public interfaces, must be included here.
+ */
+#define EIC_INSIDE_LIBEIC_H
+#include "EicCbor.h"
+#include "EicOps.h"
+#include "EicPresentation.h"
+#include "EicProvisioning.h"
+#undef EIC_INSIDE_LIBEIC_H
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_LIBEIC_H
diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp
index bf95df5..c290c08 100644
--- a/identity/aidl/default/service.cpp
+++ b/identity/aidl/default/service.cpp
@@ -22,20 +22,26 @@
#include "IdentityCredentialStore.h"
+#include "FakeSecureHardwareProxy.h"
+
+using ::android::sp;
using ::android::base::InitLogging;
using ::android::base::StderrLogger;
-using aidl::android::hardware::identity::IdentityCredentialStore;
+using ::aidl::android::hardware::identity::IdentityCredentialStore;
+using ::android::hardware::identity::FakeSecureHardwareProxyFactory;
+using ::android::hardware::identity::SecureHardwareProxyFactory;
int main(int /*argc*/, char* argv[]) {
InitLogging(argv, StderrLogger);
+ sp<SecureHardwareProxyFactory> hwProxyFactory = new FakeSecureHardwareProxyFactory();
+
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<IdentityCredentialStore> store =
- ndk::SharedRefBase::make<IdentityCredentialStore>();
+ ndk::SharedRefBase::make<IdentityCredentialStore>(hwProxyFactory);
const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default";
- LOG(INFO) << "instance: " << instance;
binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str());
CHECK(status == STATUS_OK);
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 56e17ba..1629a0c 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -182,7 +182,7 @@
false /* testCredential */));
// Verify set a large number of profile count and entry count is ok
- const vector<int32_t> entryCounts = {3000};
+ const vector<int32_t> entryCounts = {255};
writableCredential->setExpectedProofOfProvisioningSize(123456);
result = writableCredential->startPersonalization(25, entryCounts);
EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 77b795b..093120d 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -935,18 +935,19 @@
optional<vector<vector<uint8_t>>> createAttestation(
const EVP_PKEY* key, const vector<uint8_t>& applicationId, const vector<uint8_t>& challenge,
uint64_t activeTimeMilliSeconds, uint64_t expireTimeMilliSeconds, bool isTestCredential) {
- const keymaster_cert_chain_t* attestation_chain =
- ::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
- if (attestation_chain == nullptr) {
- LOG(ERROR) << "Error getting attestation chain";
+ keymaster_error_t error;
+ ::keymaster::CertificateChain attestation_chain =
+ ::keymaster::getAttestationChain(KM_ALGORITHM_EC, &error);
+ if (KM_ERROR_OK != error) {
+ LOG(ERROR) << "Error getting attestation chain " << error;
return {};
}
if (expireTimeMilliSeconds == 0) {
- if (attestation_chain->entry_count < 1) {
+ if (attestation_chain.entry_count < 1) {
LOG(ERROR) << "Expected at least one entry in attestation chain";
return {};
}
- keymaster_blob_t* bcBlob = &(attestation_chain->entries[0]);
+ keymaster_blob_t* bcBlob = &(attestation_chain.entries[0]);
const uint8_t* bcData = bcBlob->data;
auto bc = X509_Ptr(d2i_X509(nullptr, &bcData, bcBlob->data_length));
time_t bcNotAfter;
@@ -1015,34 +1016,30 @@
}
::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
- keymaster_error_t error;
- ::keymaster::CertChainPtr cert_chain_out;
-
// Pretend to be implemented in a trusted environment just so we can pass
// the VTS tests. Of course, this is a pretend-only game since hopefully no
// relying party is ever going to trust our batch key and those keys above
// it.
- //
::keymaster::PureSoftKeymasterContext context(::keymaster::KmVersion::KEYMASTER_4_1,
KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
- error = generate_attestation_from_EVP(key, swEnforced, hwEnforced, auth_set, context,
- *attestation_chain, *attestation_signing_key,
- &cert_chain_out);
+ ::keymaster::CertificateChain cert_chain_out = generate_attestation_from_EVP(
+ key, swEnforced, hwEnforced, auth_set, context, move(attestation_chain),
+ *attestation_signing_key, &error);
- if (KM_ERROR_OK != error || !cert_chain_out) {
+ if (KM_ERROR_OK != error) {
LOG(ERROR) << "Error generate attestation from EVP key" << error;
return {};
}
- // translate certificate format from keymaster_cert_chain_t to vector<uint8_t>.
+ // translate certificate format from keymaster_cert_chain_t to vector<vector<uint8_t>>.
vector<vector<uint8_t>> attestationCertificate;
- for (int i = 0; i < cert_chain_out->entry_count; i++) {
+ for (int i = 0; i < cert_chain_out.entry_count; i++) {
attestationCertificate.insert(
attestationCertificate.end(),
vector<uint8_t>(
- cert_chain_out->entries[i].data,
- cert_chain_out->entries[i].data + cert_chain_out->entries[i].data_length));
+ cert_chain_out.entries[i].data,
+ cert_chain_out.entries[i].data + cert_chain_out.entries[i].data_length));
}
return attestationCertificate;
diff --git a/keymaster/4.1/default/OWNERS b/keymaster/4.1/default/OWNERS
index 335660d..2b2ad2a 100644
--- a/keymaster/4.1/default/OWNERS
+++ b/keymaster/4.1/default/OWNERS
@@ -1,2 +1,4 @@
+jbires@google.com
jdanis@google.com
swillden@google.com
+zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/support/OWNERS b/keymaster/4.1/support/OWNERS
index a9efe66..2b2ad2a 100644
--- a/keymaster/4.1/support/OWNERS
+++ b/keymaster/4.1/support/OWNERS
@@ -1,3 +1,4 @@
+jbires@google.com
jdanis@google.com
swillden@google.com
-jbires@google.com
+zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/vts/OWNERS b/keymaster/4.1/vts/OWNERS
index 335660d..2b2ad2a 100644
--- a/keymaster/4.1/vts/OWNERS
+++ b/keymaster/4.1/vts/OWNERS
@@ -1,2 +1,4 @@
+jbires@google.com
jdanis@google.com
swillden@google.com
+zeuthen@google.com
\ No newline at end of file
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/IKeyMintDevice.aidl b/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/IKeyMintDevice.aidl
deleted file mode 100644
index 1616622..0000000
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/IKeyMintDevice.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files 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.keymint;
-@VintfStability
-interface IKeyMintDevice {
- android.hardware.keymint.KeyMintHardwareInfo getHardwareInfo();
- android.hardware.keymint.VerificationToken verifyAuthorization(in long challenge, in android.hardware.keymint.HardwareAuthToken token);
- void addRngEntropy(in byte[] data);
- void generateKey(in android.hardware.keymint.KeyParameter[] keyParams, out android.hardware.keymint.ByteArray generatedKeyBlob, out android.hardware.keymint.KeyCharacteristics generatedKeyCharacteristics, out android.hardware.keymint.Certificate[] outCertChain);
- void importKey(in android.hardware.keymint.KeyParameter[] inKeyParams, in android.hardware.keymint.KeyFormat inKeyFormat, in byte[] inKeyData, out android.hardware.keymint.ByteArray outImportedKeyBlob, out android.hardware.keymint.KeyCharacteristics outImportedKeyCharacteristics, out android.hardware.keymint.Certificate[] outCertChain);
- void importWrappedKey(in byte[] inWrappedKeyData, in byte[] inWrappingKeyBlob, in byte[] inMaskingKey, in android.hardware.keymint.KeyParameter[] inUnwrappingParams, in long inPasswordSid, in long inBiometricSid, out android.hardware.keymint.ByteArray outImportedKeyBlob, out android.hardware.keymint.KeyCharacteristics outImportedKeyCharacteristics);
- byte[] upgradeKey(in byte[] inKeyBlobToUpgrade, in android.hardware.keymint.KeyParameter[] inUpgradeParams);
- void deleteKey(in byte[] inKeyBlob);
- void deleteAllKeys();
- void destroyAttestationIds();
- android.hardware.keymint.BeginResult begin(in android.hardware.keymint.KeyPurpose inPurpose, in byte[] inKeyBlob, in android.hardware.keymint.KeyParameter[] inParams, in android.hardware.keymint.HardwareAuthToken inAuthToken);
- const int AUTH_TOKEN_MAC_LENGTH = 32;
-}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/IKeyMintOperation.aidl b/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/IKeyMintOperation.aidl
deleted file mode 100644
index 5327345..0000000
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/IKeyMintOperation.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files 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.keymint;
-@VintfStability
-interface IKeyMintOperation {
- int update(in @nullable android.hardware.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable android.hardware.keymint.HardwareAuthToken inAuthToken, in @nullable android.hardware.keymint.VerificationToken inVerificationToken, out @nullable android.hardware.keymint.KeyParameterArray outParams, out @nullable android.hardware.keymint.ByteArray output);
- byte[] finish(in @nullable android.hardware.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable byte[] inSignature, in @nullable android.hardware.keymint.HardwareAuthToken authToken, in @nullable android.hardware.keymint.VerificationToken inVerificationToken, out @nullable android.hardware.keymint.KeyParameterArray outParams);
- void abort();
-}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyDerivationFunction.aidl b/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyDerivationFunction.aidl
deleted file mode 100644
index 8e2c774..0000000
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyDerivationFunction.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files 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.keymint;
-@Backing(type="int") @VintfStability
-enum KeyDerivationFunction {
- NONE = 0,
- RFC5869_SHA256 = 1,
- ISO18033_2_KDF1_SHA1 = 2,
- ISO18033_2_KDF1_SHA256 = 3,
- ISO18033_2_KDF2_SHA1 = 4,
- ISO18033_2_KDF2_SHA256 = 5,
-}
diff --git a/keymint/aidl/android/hardware/keymint/KeyDerivationFunction.aidl b/keymint/aidl/android/hardware/keymint/KeyDerivationFunction.aidl
deleted file mode 100644
index 1eba446..0000000
--- a/keymint/aidl/android/hardware/keymint/KeyDerivationFunction.aidl
+++ /dev/null
@@ -1,37 +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.keymint;
-
-/**
- * Key derivation functions, mostly used in ECIES.
- */
-@VintfStability
-@Backing(type="int")
-enum KeyDerivationFunction {
- /** Do not apply a key derivation function; use the raw agreed key */
- NONE = 0,
- /** HKDF defined in RFC 5869 with SHA256 */
- RFC5869_SHA256 = 1,
- /** KDF1 defined in ISO 18033-2 with SHA1 */
- ISO18033_2_KDF1_SHA1 = 2,
- /** KDF1 defined in ISO 18033-2 with SHA256 */
- ISO18033_2_KDF1_SHA256 = 3,
- /** KDF2 defined in ISO 18033-2 with SHA1 */
- ISO18033_2_KDF2_SHA1 = 4,
- /** KDF2 defined in ISO 18033-2 with SHA256 */
- ISO18033_2_KDF2_SHA256 = 5,
-}
diff --git a/keymint/aidl/android/hardware/keymint/KeyParameter.aidl b/keymint/aidl/android/hardware/keymint/KeyParameter.aidl
deleted file mode 100644
index d58e4aa..0000000
--- a/keymint/aidl/android/hardware/keymint/KeyParameter.aidl
+++ /dev/null
@@ -1,56 +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.keymint;
-
-
-import android.hardware.keymint.Algorithm;
-import android.hardware.keymint.BlockMode;
-import android.hardware.keymint.Digest;
-import android.hardware.keymint.EcCurve;
-import android.hardware.keymint.HardwareAuthenticatorType;
-import android.hardware.keymint.KeyDerivationFunction;
-import android.hardware.keymint.KeyOrigin;
-import android.hardware.keymint.KeyPurpose;
-import android.hardware.keymint.PaddingMode;
-import android.hardware.keymint.SecurityLevel;
-import android.hardware.keymint.Tag;
-
-
-/**
- * Identifies the key authorization parameters to be used with keyMint. This is usually
- * provided as an array of KeyParameters to IKeyMintDevice or Operation.
- *
- * TODO(seleneh): Union was not supported in aidl when this cl is first drafted. So we just had
- * the Tags, and bool, int, long, int[], and we will cast to the appropate types base on the
- * Tag value. We need to update this defination to distingish Algorithm, BlockMode,
- * PaddingMode, KeyOrigin...etc later, as union support is recently added to aidl.
- * b/173253030
- */
-@VintfStability
-parcelable KeyParameter {
- /**
- * Identify what type of key parameter this parcelable actually holds, and based on the type
- * of tag is int, long, bool, or byte[], one of the fields below will be referenced.
- */
- Tag tag;
-
- boolean boolValue;
- int integer;
- long longInteger;
- // TODO: change this to nullable.
- byte[] blob;
-}
diff --git a/keymint/aidl/default/Android.bp b/keymint/aidl/default/Android.bp
deleted file mode 100644
index 539ca47..0000000
--- a/keymint/aidl/default/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-cc_binary {
- name: "android.hardware.keymint@1.0-service",
- relative_install_path: "hw",
- init_rc: ["android.hardware.keymint@1.0-service.rc"],
- vintf_fragments: ["android.hardware.keymint@1.0-service.xml"],
- vendor: true,
- cflags: [
- "-Wall",
- "-Wextra",
- ],
- shared_libs: [
- "android.hardware.keymint-ndk_platform",
- "libbase",
- "libbinder_ndk",
- "libcppbor",
- "libcrypto",
- "liblog",
- "libkeymaster_portable",
- "libkeymint1",
- "libpuresoftkeymasterdevice",
- "libutils",
- ],
- srcs: [
- "service.cpp",
- ],
-}
diff --git a/keymint/aidl/default/android.hardware.keymint@1.0-service.rc b/keymint/aidl/default/android.hardware.keymint@1.0-service.rc
deleted file mode 100644
index 92dce88..0000000
--- a/keymint/aidl/default/android.hardware.keymint@1.0-service.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service vendor.keymint-default /vendor/bin/hw/android.hardware.keymint@1.0-service
- class early_hal
- user nobody
diff --git a/keymint/support/authorization_set.cpp b/keymint/support/authorization_set.cpp
deleted file mode 100644
index 9fc4e13..0000000
--- a/keymint/support/authorization_set.cpp
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#include <keymintSupport/authorization_set.h>
-
-#include <assert.h>
-
-#include <android-base/logging.h>
-#include <sstream>
-
-#include <android/hardware/keymint/Algorithm.h>
-#include <android/hardware/keymint/BlockMode.h>
-#include <android/hardware/keymint/Digest.h>
-#include <android/hardware/keymint/KeyParameter.h>
-#include <android/hardware/keymint/KeyPurpose.h>
-#include <android/hardware/keymint/TagType.h>
-
-namespace android {
-namespace hardware {
-namespace keymint {
-
-void AuthorizationSet::Sort() {
- std::sort(data_.begin(), data_.end());
-}
-
-void AuthorizationSet::Deduplicate() {
- if (data_.empty()) return;
-
- Sort();
- std::vector<KeyParameter> result;
-
- auto curr = data_.begin();
- auto prev = curr++;
- for (; curr != data_.end(); ++prev, ++curr) {
- if (prev->tag == Tag::INVALID) continue;
-
- if (*prev != *curr) {
- result.push_back(std::move(*prev));
- }
- }
- result.push_back(std::move(*prev));
-
- std::swap(data_, result);
-}
-
-void AuthorizationSet::Union(const AuthorizationSet& other) {
- data_.insert(data_.end(), other.data_.begin(), other.data_.end());
- Deduplicate();
-}
-
-void AuthorizationSet::Subtract(const AuthorizationSet& other) {
- Deduplicate();
-
- auto i = other.begin();
- while (i != other.end()) {
- int pos = -1;
- do {
- pos = find(i->tag, pos);
- if (pos != -1 && (*i == data_[pos])) {
- data_.erase(data_.begin() + pos);
- break;
- }
- } while (pos != -1);
- ++i;
- }
-}
-
-void AuthorizationSet::Filter(std::function<bool(const KeyParameter&)> doKeep) {
- std::vector<KeyParameter> result;
- for (auto& param : data_) {
- if (doKeep(param)) {
- result.push_back(std::move(param));
- }
- }
- std::swap(data_, result);
-}
-
-KeyParameter& AuthorizationSet::operator[](int at) {
- return data_[at];
-}
-
-const KeyParameter& AuthorizationSet::operator[](int at) const {
- return data_[at];
-}
-
-void AuthorizationSet::Clear() {
- data_.clear();
-}
-
-size_t AuthorizationSet::GetTagCount(Tag tag) const {
- size_t count = 0;
- for (int pos = -1; (pos = find(tag, pos)) != -1;) ++count;
- return count;
-}
-
-int AuthorizationSet::find(Tag tag, int begin) const {
- auto iter = data_.begin() + (1 + begin);
-
- while (iter != data_.end() && iter->tag != tag) ++iter;
-
- if (iter != data_.end()) return iter - data_.begin();
- return -1;
-}
-
-bool AuthorizationSet::erase(int index) {
- auto pos = data_.begin() + index;
- if (pos != data_.end()) {
- data_.erase(pos);
- return true;
- }
- return false;
-}
-
-NullOr<const KeyParameter&> AuthorizationSet::GetEntry(Tag tag) const {
- int pos = find(tag);
- if (pos == -1) return {};
- return data_[pos];
-}
-
-/**
- * Persistent format is:
- * | 32 bit indirect_size |
- * --------------------------------
- * | indirect_size bytes of data | this is where the blob data is stored
- * --------------------------------
- * | 32 bit element_count | number of entries
- * | 32 bit elements_size | total bytes used by entries (entries have variable length)
- * --------------------------------
- * | elementes_size bytes of data | where the elements are stored
- */
-
-/**
- * Persistent format of blobs and bignums:
- * | 32 bit tag |
- * | 32 bit blob_length |
- * | 32 bit indirect_offset |
- */
-
-struct OutStreams {
- std::ostream& indirect;
- std::ostream& elements;
- size_t skipped;
-};
-
-OutStreams& serializeParamValue(OutStreams& out, const vector<uint8_t>& blob) {
- uint32_t buffer;
-
- // write blob_length
- auto blob_length = blob.size();
- if (blob_length > std::numeric_limits<uint32_t>::max()) {
- out.elements.setstate(std::ios_base::badbit);
- return out;
- }
- buffer = blob_length;
- out.elements.write(reinterpret_cast<const char*>(&buffer), sizeof(uint32_t));
-
- // write indirect_offset
- auto offset = out.indirect.tellp();
- if (offset < 0 || offset > std::numeric_limits<uint32_t>::max() ||
- uint32_t(offset) + uint32_t(blob_length) < uint32_t(offset)) { // overflow check
- out.elements.setstate(std::ios_base::badbit);
- return out;
- }
- buffer = offset;
- out.elements.write(reinterpret_cast<const char*>(&buffer), sizeof(uint32_t));
-
- // write blob to indirect stream
- if (blob_length) out.indirect.write(reinterpret_cast<const char*>(&blob[0]), blob_length);
-
- return out;
-}
-
-template <typename T>
-OutStreams& serializeParamValue(OutStreams& out, const T& value) {
- out.elements.write(reinterpret_cast<const char*>(&value), sizeof(T));
- return out;
-}
-
-OutStreams& serialize(TAG_INVALID_t&&, OutStreams& out, const KeyParameter&) {
- // skip invalid entries.
- ++out.skipped;
- return out;
-}
-template <typename T>
-OutStreams& serialize(T ttag, OutStreams& out, const KeyParameter& param) {
- out.elements.write(reinterpret_cast<const char*>(¶m.tag), sizeof(int32_t));
- return serializeParamValue(out, accessTagValue(ttag, param));
-}
-
-template <typename... T>
-struct choose_serializer;
-template <typename... Tags>
-struct choose_serializer<MetaList<Tags...>> {
- static OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
- return choose_serializer<Tags...>::serialize(out, param);
- }
-};
-
-template <>
-struct choose_serializer<> {
- static OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
- LOG(WARNING) << "Trying to serialize unknown tag " << unsigned(param.tag)
- << ". Did you forget to add it to all_tags_t?";
- ++out.skipped;
- return out;
- }
-};
-
-template <TagType tag_type, Tag tag, typename... Tail>
-struct choose_serializer<android::hardware::keymint::TypedTag<tag_type, tag>, Tail...> {
- static OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
- if (param.tag == tag) {
- return android::hardware::keymint::serialize(TypedTag<tag_type, tag>(), out, param);
- } else {
- return choose_serializer<Tail...>::serialize(out, param);
- }
- }
-};
-
-OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
- return choose_serializer<all_tags_t>::serialize(out, param);
-}
-
-std::ostream& serialize(std::ostream& out, const std::vector<KeyParameter>& params) {
- std::stringstream indirect;
- std::stringstream elements;
- OutStreams streams = {indirect, elements, 0};
- for (const auto& param : params) {
- serialize(streams, param);
- }
- if (indirect.bad() || elements.bad()) {
- out.setstate(std::ios_base::badbit);
- return out;
- }
- auto pos = indirect.tellp();
- if (pos < 0 || pos > std::numeric_limits<uint32_t>::max()) {
- out.setstate(std::ios_base::badbit);
- return out;
- }
- uint32_t indirect_size = pos;
- pos = elements.tellp();
- if (pos < 0 || pos > std::numeric_limits<uint32_t>::max()) {
- out.setstate(std::ios_base::badbit);
- return out;
- }
- uint32_t elements_size = pos;
- uint32_t element_count = params.size() - streams.skipped;
-
- out.write(reinterpret_cast<const char*>(&indirect_size), sizeof(uint32_t));
-
- pos = out.tellp();
- if (indirect_size) out << indirect.rdbuf();
- assert(out.tellp() - pos == indirect_size);
-
- out.write(reinterpret_cast<const char*>(&element_count), sizeof(uint32_t));
- out.write(reinterpret_cast<const char*>(&elements_size), sizeof(uint32_t));
-
- pos = out.tellp();
- if (elements_size) out << elements.rdbuf();
- assert(out.tellp() - pos == elements_size);
-
- return out;
-}
-
-struct InStreams {
- std::istream& indirect;
- std::istream& elements;
- size_t invalids;
-};
-
-InStreams& deserializeParamValue(InStreams& in, vector<uint8_t>* blob) {
- uint32_t blob_length = 0;
- uint32_t offset = 0;
- in.elements.read(reinterpret_cast<char*>(&blob_length), sizeof(uint32_t));
- blob->resize(blob_length);
- in.elements.read(reinterpret_cast<char*>(&offset), sizeof(uint32_t));
- in.indirect.seekg(offset);
- in.indirect.read(reinterpret_cast<char*>(&(*blob)[0]), blob->size());
- return in;
-}
-
-template <typename T>
-InStreams& deserializeParamValue(InStreams& in, T* value) {
- in.elements.read(reinterpret_cast<char*>(value), sizeof(T));
- return in;
-}
-
-InStreams& deserialize(TAG_INVALID_t&&, InStreams& in, KeyParameter*) {
- // there should be no invalid KeyParameters but if handle them as zero sized.
- ++in.invalids;
- return in;
-}
-
-template <typename T>
-InStreams& deserialize(T&& ttag, InStreams& in, KeyParameter* param) {
- return deserializeParamValue(in, &accessTagValue(ttag, *param));
-}
-
-template <typename... T>
-struct choose_deserializer;
-template <typename... Tags>
-struct choose_deserializer<MetaList<Tags...>> {
- static InStreams& deserialize(InStreams& in, KeyParameter* param) {
- return choose_deserializer<Tags...>::deserialize(in, param);
- }
-};
-template <>
-struct choose_deserializer<> {
- static InStreams& deserialize(InStreams& in, KeyParameter*) {
- // encountered an unknown tag -> fail parsing
- in.elements.setstate(std::ios_base::badbit);
- return in;
- }
-};
-template <TagType tag_type, Tag tag, typename... Tail>
-struct choose_deserializer<TypedTag<tag_type, tag>, Tail...> {
- static InStreams& deserialize(InStreams& in, KeyParameter* param) {
- if (param->tag == tag) {
- return android::hardware::keymint::deserialize(TypedTag<tag_type, tag>(), in, param);
- } else {
- return choose_deserializer<Tail...>::deserialize(in, param);
- }
- }
-};
-
-InStreams& deserialize(InStreams& in, KeyParameter* param) {
- in.elements.read(reinterpret_cast<char*>(¶m->tag), sizeof(Tag));
- return choose_deserializer<all_tags_t>::deserialize(in, param);
-}
-
-std::istream& deserialize(std::istream& in, std::vector<KeyParameter>* params) {
- uint32_t indirect_size = 0;
- in.read(reinterpret_cast<char*>(&indirect_size), sizeof(uint32_t));
- std::string indirect_buffer(indirect_size, '\0');
- if (indirect_buffer.size() != indirect_size) {
- in.setstate(std::ios_base::badbit);
- return in;
- }
- in.read(&indirect_buffer[0], indirect_buffer.size());
-
- uint32_t element_count = 0;
- in.read(reinterpret_cast<char*>(&element_count), sizeof(uint32_t));
- uint32_t elements_size = 0;
- in.read(reinterpret_cast<char*>(&elements_size), sizeof(uint32_t));
-
- std::string elements_buffer(elements_size, '\0');
- if (elements_buffer.size() != elements_size) {
- in.setstate(std::ios_base::badbit);
- return in;
- }
- in.read(&elements_buffer[0], elements_buffer.size());
-
- if (in.bad()) return in;
-
- // TODO write one-shot stream buffer to avoid copying here
- std::stringstream indirect(indirect_buffer);
- std::stringstream elements(elements_buffer);
- InStreams streams = {indirect, elements, 0};
-
- params->resize(element_count);
-
- for (uint32_t i = 0; i < element_count; ++i) {
- deserialize(streams, &(*params)[i]);
- }
-
- /*
- * There are legacy blobs which have invalid tags in them due to a bug during serialization.
- * This makes sure that invalid tags are filtered from the result before it is returned.
- */
- if (streams.invalids > 0) {
- std::vector<KeyParameter> filtered(element_count - streams.invalids);
- auto ifiltered = filtered.begin();
- for (auto& p : *params) {
- if (p.tag != Tag::INVALID) {
- *ifiltered++ = std::move(p);
- }
- }
- *params = std::move(filtered);
- }
- return in;
-}
-
-void AuthorizationSet::Serialize(std::ostream* out) const {
- serialize(*out, data_);
-}
-
-void AuthorizationSet::Deserialize(std::istream* in) {
- deserialize(*in, &data_);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size,
- uint64_t public_exponent) {
- Authorization(TAG_ALGORITHM, Algorithm::RSA);
- Authorization(TAG_KEY_SIZE, key_size);
- Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent);
- return *this;
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) {
- Authorization(TAG_ALGORITHM, Algorithm::EC);
- Authorization(TAG_KEY_SIZE, key_size);
- return *this;
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(EcCurve curve) {
- Authorization(TAG_ALGORITHM, Algorithm::EC);
- Authorization(TAG_EC_CURVE, curve);
- return *this;
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) {
- Authorization(TAG_ALGORITHM, Algorithm::AES);
- return Authorization(TAG_KEY_SIZE, key_size);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesKey(uint32_t key_size) {
- Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES);
- return Authorization(TAG_KEY_SIZE, key_size);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) {
- Authorization(TAG_ALGORITHM, Algorithm::HMAC);
- Authorization(TAG_KEY_SIZE, key_size);
- return SigningKey();
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size,
- uint64_t public_exponent) {
- RsaKey(key_size, public_exponent);
- return SigningKey();
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size,
- uint64_t public_exponent) {
- RsaKey(key_size, public_exponent);
- return EncryptionKey();
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) {
- EcdsaKey(key_size);
- return SigningKey();
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) {
- EcdsaKey(curve);
- return SigningKey();
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) {
- AesKey(key_size);
- return EncryptionKey();
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesEncryptionKey(uint32_t key_size) {
- TripleDesKey(key_size);
- return EncryptionKey();
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() {
- Authorization(TAG_PURPOSE, KeyPurpose::SIGN);
- return Authorization(TAG_PURPOSE, KeyPurpose::VERIFY);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() {
- Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT);
- return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() {
- Authorization(TAG_DIGEST, Digest::NONE);
- return Authorization(TAG_PADDING, PaddingMode::NONE);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() {
- return Authorization(TAG_BLOCK_MODE, BlockMode::ECB);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMinMacLen(uint32_t minMacLength) {
- return BlockMode(BlockMode::GCM)
- .Padding(PaddingMode::NONE)
- .Authorization(TAG_MIN_MAC_LENGTH, minMacLength);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMacLen(uint32_t macLength) {
- return BlockMode(BlockMode::GCM)
- .Padding(PaddingMode::NONE)
- .Authorization(TAG_MAC_LENGTH, macLength);
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::BlockMode(
- std::initializer_list<android::hardware::keymint::BlockMode> blockModes) {
- for (auto mode : blockModes) {
- push_back(TAG_BLOCK_MODE, mode);
- }
- return *this;
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::Digest(
- std::vector<android::hardware::keymint::Digest> digests) {
- for (auto digest : digests) {
- push_back(TAG_DIGEST, digest);
- }
- return *this;
-}
-
-AuthorizationSetBuilder& AuthorizationSetBuilder::Padding(
- std::initializer_list<PaddingMode> paddingModes) {
- for (auto paddingMode : paddingModes) {
- push_back(TAG_PADDING, paddingMode);
- }
- return *this;
-}
-
-} // namespace keymint
-} // namespace hardware
-} // namespace android
diff --git a/keymint/support/include/keymintSupport/keymint_tags.h b/keymint/support/include/keymintSupport/keymint_tags.h
deleted file mode 100644
index f1060a9..0000000
--- a/keymint/support/include/keymintSupport/keymint_tags.h
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2019 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 HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEYMINT_TAGS_H_
-#define HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEYMINT_TAGS_H_
-
-#include <android/hardware/keymint/Algorithm.h>
-#include <android/hardware/keymint/BlockMode.h>
-#include <android/hardware/keymint/Digest.h>
-#include <android/hardware/keymint/EcCurve.h>
-#include <android/hardware/keymint/HardwareAuthenticatorType.h>
-#include <android/hardware/keymint/KeyOrigin.h>
-#include <android/hardware/keymint/KeyParameter.h>
-#include <android/hardware/keymint/KeyPurpose.h>
-#include <android/hardware/keymint/PaddingMode.h>
-#include <android/hardware/keymint/SecurityLevel.h>
-#include <android/hardware/keymint/Tag.h>
-#include <android/hardware/keymint/TagType.h>
-
-namespace android::hardware::keymint {
-
-using android::hardware::keymint::KeyParameter;
-using android::hardware::keymint::Tag;
-using android::hardware::keymint::TagType;
-
-// The following create the numeric values that KM_TAG_PADDING and KM_TAG_DIGEST used to have. We
-// need these old values to be able to support old keys that use them.
-// TODO(seleneh) we should delete this code when we stop supporting keymaster1
-// and deletes it.
-static const int32_t KM_TAG_DIGEST_OLD = static_cast<int32_t>(TagType::ENUM) | 5;
-static const int32_t KM_TAG_PADDING_OLD = static_cast<int32_t>(TagType::ENUM) | 7;
-
-constexpr TagType typeFromTag(Tag tag) {
- return static_cast<TagType>(static_cast<uint32_t>(tag) & static_cast<uint32_t>(0xf0000000));
-}
-
-/**
- * TypedTag is a templatized version of Tag, which provides compile-time checking of
- * keymint tag types. Instances are convertible to Tag, so they can be used wherever
- * Tag is expected, and because they encode the tag type it's possible to create
- * function overloads that only operate on tags with a particular type.
- */
-template <TagType tag_type, Tag tag>
-struct TypedTag {
- inline TypedTag() {
- // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type
- // 'tag_type'. Attempting to instantiate a tag with the wrong type will result in a compile
- // error (no match for template specialization StaticAssert<false>), with no run-time cost.
- static_assert(typeFromTag(tag) == tag_type, "mismatch between tag and tag_type");
- }
- operator Tag() const { return tag; }
- int32_t maskedTag() { return static_cast<uint32_t>(tag) & 0x0FFFFFFF; }
-};
-
-template <Tag tag>
-struct Tag2TypedTag {
- typedef TypedTag<typeFromTag(tag), tag> type;
-};
-
-#define DECLARE_TYPED_TAG(name) \
- typedef typename Tag2TypedTag<Tag::name>::type TAG_##name##_t; \
- static TAG_##name##_t TAG_##name;
-
-DECLARE_TYPED_TAG(ACTIVE_DATETIME);
-DECLARE_TYPED_TAG(ALGORITHM);
-DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
-DECLARE_TYPED_TAG(APPLICATION_DATA);
-DECLARE_TYPED_TAG(APPLICATION_ID);
-DECLARE_TYPED_TAG(ASSOCIATED_DATA);
-DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
-DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
-DECLARE_TYPED_TAG(ATTESTATION_ID_BRAND);
-DECLARE_TYPED_TAG(ATTESTATION_ID_DEVICE);
-DECLARE_TYPED_TAG(ATTESTATION_ID_PRODUCT);
-DECLARE_TYPED_TAG(ATTESTATION_ID_MANUFACTURER);
-DECLARE_TYPED_TAG(ATTESTATION_ID_MODEL);
-DECLARE_TYPED_TAG(AUTH_TIMEOUT);
-DECLARE_TYPED_TAG(BLOCK_MODE);
-DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
-DECLARE_TYPED_TAG(BOOT_PATCHLEVEL);
-DECLARE_TYPED_TAG(CALLER_NONCE);
-DECLARE_TYPED_TAG(CONFIRMATION_TOKEN);
-DECLARE_TYPED_TAG(CREATION_DATETIME);
-DECLARE_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION);
-DECLARE_TYPED_TAG(DIGEST);
-DECLARE_TYPED_TAG(EARLY_BOOT_ONLY);
-DECLARE_TYPED_TAG(EC_CURVE);
-DECLARE_TYPED_TAG(HARDWARE_TYPE);
-DECLARE_TYPED_TAG(IDENTITY_CREDENTIAL_KEY);
-DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
-DECLARE_TYPED_TAG(INVALID);
-DECLARE_TYPED_TAG(KEY_SIZE);
-DECLARE_TYPED_TAG(MAC_LENGTH);
-DECLARE_TYPED_TAG(MAX_USES_PER_BOOT);
-DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
-DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
-DECLARE_TYPED_TAG(NONCE);
-DECLARE_TYPED_TAG(NO_AUTH_REQUIRED);
-DECLARE_TYPED_TAG(ORIGIN);
-DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
-DECLARE_TYPED_TAG(OS_PATCHLEVEL);
-DECLARE_TYPED_TAG(OS_VERSION);
-DECLARE_TYPED_TAG(PADDING);
-DECLARE_TYPED_TAG(PURPOSE);
-DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
-DECLARE_TYPED_TAG(ROLLBACK_RESISTANCE);
-DECLARE_TYPED_TAG(ROOT_OF_TRUST);
-DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
-DECLARE_TYPED_TAG(STORAGE_KEY);
-DECLARE_TYPED_TAG(TRUSTED_CONFIRMATION_REQUIRED);
-DECLARE_TYPED_TAG(TRUSTED_USER_PRESENCE_REQUIRED);
-DECLARE_TYPED_TAG(UNIQUE_ID);
-DECLARE_TYPED_TAG(UNLOCKED_DEVICE_REQUIRED);
-DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
-DECLARE_TYPED_TAG(USER_AUTH_TYPE);
-DECLARE_TYPED_TAG(USER_ID);
-DECLARE_TYPED_TAG(USER_SECURE_ID);
-DECLARE_TYPED_TAG(VENDOR_PATCHLEVEL);
-
-template <typename... Elems>
-struct MetaList {};
-
-using all_tags_t = MetaList<
- TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t,
- TAG_RSA_PUBLIC_EXPONENT_t, TAG_INCLUDE_UNIQUE_ID_t, TAG_ACTIVE_DATETIME_t,
- TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t,
- TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_USER_ID_t, TAG_USER_SECURE_ID_t,
- TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t, TAG_ALLOW_WHILE_ON_BODY_t,
- TAG_UNLOCKED_DEVICE_REQUIRED_t, TAG_APPLICATION_ID_t, TAG_APPLICATION_DATA_t,
- TAG_CREATION_DATETIME_t, TAG_ROLLBACK_RESISTANCE_t, TAG_HARDWARE_TYPE_t,
- TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t, TAG_BOOTLOADER_ONLY_t,
- TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t, TAG_ATTESTATION_CHALLENGE_t,
- TAG_ATTESTATION_APPLICATION_ID_t, TAG_ATTESTATION_ID_BRAND_t, TAG_ATTESTATION_ID_DEVICE_t,
- TAG_ATTESTATION_ID_PRODUCT_t, TAG_ATTESTATION_ID_MANUFACTURER_t, TAG_ATTESTATION_ID_MODEL_t,
- TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t,
- TAG_DIGEST_t, TAG_PADDING_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t,
- TAG_BOOT_PATCHLEVEL_t, TAG_VENDOR_PATCHLEVEL_t, TAG_TRUSTED_CONFIRMATION_REQUIRED_t,
- TAG_TRUSTED_USER_PRESENCE_REQUIRED_t>;
-
-template <typename TypedTagType>
-struct TypedTag2ValueType;
-
-#define MAKE_TAG_VALUE_ACCESSOR(tag_type, field_name) \
- template <Tag tag> \
- struct TypedTag2ValueType<TypedTag<tag_type, tag>> { \
- typedef decltype(static_cast<KeyParameter*>(nullptr)->field_name) type; \
- }; \
- template <Tag tag> \
- inline auto accessTagValue(TypedTag<tag_type, tag>, const KeyParameter& param) \
- ->const decltype(param.field_name)& { \
- return param.field_name; \
- } \
- template <Tag tag> \
- inline auto accessTagValue(TypedTag<tag_type, tag>, KeyParameter& param) \
- ->decltype(param.field_name)& { \
- return param.field_name; \
- }
-
-MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG, longInteger)
-MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG_REP, longInteger)
-MAKE_TAG_VALUE_ACCESSOR(TagType::DATE, longInteger)
-MAKE_TAG_VALUE_ACCESSOR(TagType::UINT, integer)
-MAKE_TAG_VALUE_ACCESSOR(TagType::UINT_REP, integer)
-MAKE_TAG_VALUE_ACCESSOR(TagType::BOOL, boolValue)
-MAKE_TAG_VALUE_ACCESSOR(TagType::BYTES, blob)
-MAKE_TAG_VALUE_ACCESSOR(TagType::BIGNUM, blob)
-
-// TODO(seleneh) change these MAKE_TAG_ENUM_VALUE_ACCESSOR back to the 2 parameter
-// version when aidl supports union
-#define MAKE_TAG_ENUM_VALUE_ACCESSOR(typed_tag, field_name, field_type) \
- template <> \
- struct TypedTag2ValueType<decltype(typed_tag)> { \
- typedef field_type type; \
- }; \
- inline auto accessTagValue(decltype(typed_tag), const KeyParameter& param) \
- ->const field_type& { \
- return *reinterpret_cast<const field_type*>(¶m.field_name); \
- } \
- inline auto accessTagValue(decltype(typed_tag), KeyParameter& param)->field_type& { \
- return *reinterpret_cast<field_type*>(¶m.field_name); \
- }
-
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ALGORITHM, integer, Algorithm)
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOCK_MODE, integer, BlockMode)
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_DIGEST, integer, Digest)
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_EC_CURVE, integer, EcCurve)
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ORIGIN, integer, KeyOrigin)
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PADDING, integer, PaddingMode)
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PURPOSE, integer, KeyPurpose)
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_USER_AUTH_TYPE, integer, HardwareAuthenticatorType)
-MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_HARDWARE_TYPE, integer, SecurityLevel)
-
-template <TagType tag_type, Tag tag, typename ValueT>
-inline KeyParameter makeKeyParameter(TypedTag<tag_type, tag> ttag, ValueT&& value) {
- KeyParameter param;
- param.tag = tag;
- param.longInteger = 0;
- accessTagValue(ttag, param) = std::forward<ValueT>(value);
- return param;
-}
-
-// the boolean case
-template <Tag tag>
-inline KeyParameter makeKeyParameter(TypedTag<TagType::BOOL, tag>) {
- KeyParameter param;
- param.tag = tag;
- param.boolValue = true;
- return param;
-}
-
-template <typename... Pack>
-struct FirstOrNoneHelper;
-template <typename First>
-struct FirstOrNoneHelper<First> {
- typedef First type;
-};
-template <>
-struct FirstOrNoneHelper<> {
- struct type {};
-};
-
-template <typename... Pack>
-using FirstOrNone = typename FirstOrNoneHelper<Pack...>::type;
-
-template <TagType tag_type, Tag tag, typename... Args>
-inline KeyParameter Authorization(TypedTag<tag_type, tag> ttag, Args&&... args) {
- static_assert(tag_type != TagType::BOOL || (sizeof...(args) == 0),
- "TagType::BOOL Authorizations do not take parameters. Presence is truth.");
- static_assert(tag_type == TagType::BOOL || (sizeof...(args) == 1),
- "Authorization other then TagType::BOOL take exactly one parameter.");
- static_assert(
- tag_type == TagType::BOOL ||
- std::is_convertible<
- std::remove_cv_t<std::remove_reference_t<FirstOrNone<Args...>>>,
- typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>::value,
- "Invalid argument type for given tag.");
-
- return makeKeyParameter(ttag, std::forward<Args>(args)...);
-}
-
-/**
- * This class wraps a (mostly return) value and stores whether or not the wrapped value is valid out
- * of band. Note that if the wrapped value is a reference it is unsafe to access the value if
- * !isOk(). If the wrapped type is a pointer or value and !isOk(), it is still safe to access the
- * wrapped value. In this case the pointer will be NULL though, and the value will be default
- * constructed.
- *
- * TODO(seleneh) replace this with std::optional.
- */
-template <typename ValueT>
-class NullOr {
- using internal_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value,
- std::remove_reference_t<ValueT>*, ValueT>;
-
- struct pointer_initializer {
- static std::nullptr_t init() { return nullptr; }
- };
- struct value_initializer {
- static ValueT init() { return ValueT(); }
- };
- struct value_pointer_deref_t {
- static ValueT& deref(ValueT& v) { return v; }
- };
- struct reference_deref_t {
- static auto& deref(internal_t v) { return *v; }
- };
- using initializer_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value ||
- std::is_pointer<ValueT>::value,
- pointer_initializer, value_initializer>;
- using deref_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value, reference_deref_t,
- value_pointer_deref_t>;
-
- public:
- NullOr() : value_(initializer_t::init()), null_(true) {}
- template <typename T>
- NullOr(T&& value, typename std::enable_if<
- !std::is_lvalue_reference<ValueT>::value &&
- std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
- int>::type = 0)
- : value_(std::forward<ValueT>(value)), null_(false) {}
- template <typename T>
- NullOr(T& value, typename std::enable_if<
- std::is_lvalue_reference<ValueT>::value &&
- std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
- int>::type = 0)
- : value_(&value), null_(false) {}
-
- bool isOk() const { return !null_; }
-
- const ValueT& value() const& { return deref_t::deref(value_); }
- ValueT& value() & { return deref_t::deref(value_); }
- ValueT&& value() && { return std::move(deref_t::deref(value_)); }
-
- private:
- internal_t value_;
- bool null_;
-};
-
-template <typename T>
-std::remove_reference_t<T> NullOrOr(T&& v) {
- if (v.isOk()) return v;
- return {};
-}
-
-template <typename Head, typename... Tail>
-std::remove_reference_t<Head> NullOrOr(Head&& head, Tail&&... tail) {
- if (head.isOk()) return head;
- return NullOrOr(std::forward<Tail>(tail)...);
-}
-
-template <typename Default, typename Wrapped>
-std::remove_reference_t<Wrapped> defaultOr(NullOr<Wrapped>&& optional, Default&& def) {
- static_assert(std::is_convertible<std::remove_reference_t<Default>,
- std::remove_reference_t<Wrapped>>::value,
- "Type of default value must match the type wrapped by NullOr");
- if (optional.isOk()) return optional.value();
- return def;
-}
-
-template <TagType tag_type, Tag tag>
-inline NullOr<const typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type&> authorizationValue(
- TypedTag<tag_type, tag> ttag, const KeyParameter& param) {
- if (tag != param.tag) return {};
- return accessTagValue(ttag, param);
-}
-
-} // namespace android::hardware::keymint
-
-namespace std {
-
-using namespace android::hardware::keymint;
-
-// Aidl generates KeyParameter operator<, >, ==, != for cpp translation but not ndk
-// translations. So we cannot straight forward overload these operators.
-// However we need our custom comparison for KeyParameters. So we will
-// overload std::less, equal_to instead.
-template <>
-struct std::less<KeyParameter> {
- bool operator()(const KeyParameter& a, const KeyParameter& b) const {
- if (a.tag != b.tag) return a.tag < b.tag;
- int retval;
- switch (typeFromTag(a.tag)) {
- case TagType::INVALID:
- case TagType::BOOL:
- return false;
- case TagType::ENUM:
- case TagType::ENUM_REP:
- case TagType::UINT:
- case TagType::UINT_REP:
- return a.integer < b.integer;
- case TagType::ULONG:
- case TagType::ULONG_REP:
- case TagType::DATE:
- return a.longInteger < b.longInteger;
- case TagType::BIGNUM:
- case TagType::BYTES:
- // Handle the empty cases.
- if (a.blob.size() == 0) return b.blob.size() != 0;
- if (b.blob.size() == 0) return false;
- retval = memcmp(&a.blob[0], &b.blob[0], std::min(a.blob.size(), b.blob.size()));
- // if one is the prefix of the other the longer wins
- if (retval == 0) return a.blob.size() < b.blob.size();
- // Otherwise a is less if a is less.
- else
- return retval < 0;
- }
- return false;
- }
-};
-
-template <>
-struct std::equal_to<KeyParameter> {
- bool operator()(const KeyParameter& a, const KeyParameter& b) const {
- if (a.tag != b.tag) {
- return false;
- }
- switch (typeFromTag(a.tag)) {
- case TagType::INVALID:
- case TagType::BOOL:
- return true;
- case TagType::ENUM:
- case TagType::ENUM_REP:
- case TagType::UINT:
- case TagType::UINT_REP:
- return a.integer == b.integer;
- case TagType::ULONG:
- case TagType::ULONG_REP:
- case TagType::DATE:
- return a.longInteger == b.longInteger;
- case TagType::BIGNUM:
- case TagType::BYTES:
- if (a.blob.size() != b.blob.size()) return false;
- return a.blob.size() == 0 || memcmp(&a.blob[0], &b.blob[0], a.blob.size()) == 0;
- }
- return false;
- }
-};
-
-} // namespace std
-
-#endif // HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEYMINT_TAGS_H_
diff --git a/keymint/support/key_param_output.cpp b/keymint/support/key_param_output.cpp
deleted file mode 100644
index 6e33558..0000000
--- a/keymint/support/key_param_output.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2017 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 <keymintSupport/key_param_output.h>
-
-#include <keymintSupport/keymint_tags.h>
-
-#include <iomanip>
-
-namespace android {
-namespace hardware {
-namespace keymint {
-
-using ::std::endl;
-using ::std::ostream;
-
-ostream& operator<<(ostream& os, const ::std::vector<KeyParameter>& set) {
- if (set.size() == 0) {
- os << "(Empty)" << endl;
- } else {
- os << "\n";
- for (const auto& elem : set) os << elem << endl;
- }
- return os;
-}
-
-// TODO(seleneh) update this to a parsing that looks at each tags individually
-// such as ALGORITHM BLOCK_MODE when aidl union support is added.
-ostream& operator<<(ostream& os, const KeyParameter& param) {
- os << param.tag << ": ";
- switch (typeFromTag(param.tag)) {
- case TagType::INVALID:
- return os << " Invalid";
- case TagType::ENUM_REP:
- case TagType::ENUM:
- case TagType::UINT_REP:
- case TagType::UINT:
- return os << param.integer;
- case TagType::ULONG_REP:
- case TagType::ULONG:
- case TagType::DATE:
- return os << param.longInteger;
- case TagType::BOOL:
- return os << "true";
- case TagType::BIGNUM:
- os << " Bignum: ";
- for (size_t i = 0; i < param.blob.size(); ++i) {
- os << std::hex << ::std::setw(2) << static_cast<int>(param.blob[i]) << ::std::dec;
- }
- return os;
- case TagType::BYTES:
- os << " Bytes: ";
- for (size_t i = 0; i < param.blob.size(); ++i) {
- os << ::std::hex << ::std::setw(2) << static_cast<int>(param.blob[i]) << ::std::dec;
- }
- return os;
- }
- return os << "UNKNOWN TAG TYPE!";
-}
-
-} // namespace keymint
-} // namespace hardware
-} // namespace android
diff --git a/memtrack/aidl/Android.bp b/memtrack/aidl/Android.bp
new file mode 100644
index 0000000..fe4d01b
--- /dev/null
+++ b/memtrack/aidl/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+aidl_interface {
+ name: "android.hardware.memtrack",
+ vendor_available: true,
+ srcs: ["android/hardware/memtrack/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ enabled: false,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl
similarity index 91%
copy from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl
copy to memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl
index ca55054..00abff9 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl
@@ -15,8 +15,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.keymint;
+package android.hardware.memtrack;
@VintfStability
-parcelable Certificate {
- byte[] encodedCertificate;
+parcelable DeviceInfo {
+ int id;
+ @utf8InCpp String name;
}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl
similarity index 81%
copy from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl
copy to memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl
index ca55054..844a1bb 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl
@@ -15,8 +15,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.keymint;
+package android.hardware.memtrack;
@VintfStability
-parcelable Certificate {
- byte[] encodedCertificate;
+interface IMemtrack {
+ android.hardware.memtrack.MemtrackRecord[] getMemory(in int pid, in android.hardware.memtrack.MemtrackType type);
+ android.hardware.memtrack.DeviceInfo[] getGpuDeviceInfo();
}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl
similarity index 72%
copy from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl
copy to memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl
index ca55054..09ecefc 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl
@@ -15,8 +15,18 @@
// 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.keymint;
+package android.hardware.memtrack;
@VintfStability
-parcelable Certificate {
- byte[] encodedCertificate;
+parcelable MemtrackRecord {
+ int flags;
+ long sizeInBytes;
+ const int FLAG_SMAPS_ACCOUNTED = 2;
+ const int FLAG_SMAPS_UNACCOUNTED = 4;
+ const int FLAG_SHARED = 8;
+ const int FLAG_SHARED_PSS = 16;
+ const int FLAG_PRIVATE = 32;
+ const int FLAG_SYSTEM = 64;
+ const int FLAG_DEDICATED = 128;
+ const int FLAG_NONSECURE = 256;
+ const int FLAG_SECURE = 512;
}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Digest.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl
similarity index 87%
copy from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Digest.aidl
copy to memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl
index cc4d2fd..7f3f939 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Digest.aidl
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl
@@ -15,14 +15,13 @@
// 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.keymint;
+package android.hardware.memtrack;
@Backing(type="int") @VintfStability
-enum Digest {
- NONE = 0,
- MD5 = 1,
- SHA1 = 2,
- SHA_2_224 = 3,
- SHA_2_256 = 4,
- SHA_2_384 = 5,
- SHA_2_512 = 6,
+enum MemtrackType {
+ OTHER = 0,
+ GL = 1,
+ GRAPHICS = 2,
+ MULTIMEDIA = 3,
+ CAMERA = 4,
+ NUM_TYPES = 5,
}
diff --git a/keymint/aidl/android/hardware/keymint/EcCurve.aidl b/memtrack/aidl/android/hardware/memtrack/DeviceInfo.aidl
similarity index 78%
copy from keymint/aidl/android/hardware/keymint/EcCurve.aidl
copy to memtrack/aidl/android/hardware/memtrack/DeviceInfo.aidl
index abd44b4..bcba544 100644
--- a/keymint/aidl/android/hardware/keymint/EcCurve.aidl
+++ b/memtrack/aidl/android/hardware/memtrack/DeviceInfo.aidl
@@ -14,17 +14,14 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.memtrack;
-
-/**
- * Supported EC curves, used in ECDSA
+/*
+ * Contains the device name and the device id.
*/
@VintfStability
-@Backing(type="int")
-enum EcCurve {
- P_224 = 0,
- P_256 = 1,
- P_384 = 2,
- P_521 = 3,
+parcelable DeviceInfo {
+ int id;
+ @utf8InCpp String name;
}
+
diff --git a/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl b/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
new file mode 100644
index 0000000..18587ee
--- /dev/null
+++ b/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
@@ -0,0 +1,82 @@
+/*
+ * 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.memtrack;
+
+import android.hardware.memtrack.DeviceInfo;
+import android.hardware.memtrack.MemtrackRecord;
+import android.hardware.memtrack.MemtrackType;
+
+/**
+ * The Memory Tracker HAL is designed to return information about
+ * device-specific memory usage.
+ * The primary goal is to be able to track memory that is not
+ * trackable in any other way, for example texture memory that is allocated by
+ * a process, but not mapped in to that process's address space.
+ * A secondary goal is to be able to categorize memory used by a process into
+ * GL, graphics, etc. All memory sizes must be in real memory usage,
+ * accounting for stride, bit depth, rounding up to page size, etc.
+ *
+ * Constructor for the interface should be used to perform memtrack management
+ * setup actions and must be called once before any calls to getMemory().
+ */
+@VintfStability
+interface IMemtrack {
+ /**
+ * getMemory() populates MemtrackRecord vector with the sizes of memory
+ * plus associated flags for that memory.
+ *
+ * A process collecting memory statistics will call getMemory for each
+ * combination of pid and memory type. For each memory type that it
+ * recognizes, the HAL must fill out an array of memtrack_record
+ * structures breaking down the statistics of that memory type as much as
+ * possible. For example,
+ * getMemory(<pid>, GL) might return:
+ * { { 4096, ACCOUNTED | PRIVATE | SYSTEM },
+ * { 40960, UNACCOUNTED | PRIVATE | SYSTEM },
+ * { 8192, ACCOUNTED | PRIVATE | DEDICATED },
+ * { 8192, UNACCOUNTED | PRIVATE | DEDICATED } }
+ * If the HAL cannot differentiate between SYSTEM and DEDICATED memory, it
+ * could return:
+ * { { 12288, ACCOUNTED | PRIVATE },
+ * { 49152, UNACCOUNTED | PRIVATE } }
+ *
+ * Memory must not overlap between types. For example, a graphics buffer
+ * that has been mapped into the GPU as a surface must show up when
+ * GRAPHICS is requested and not when GL
+ * is requested.
+ *
+ * @param pid process for which memory information is requested
+ * @param type memory type that information is being requested about
+ * @return vector of MemtrackRecord containing memory information
+ */
+ MemtrackRecord[] getMemory(in int pid, in MemtrackType type);
+
+ /**
+ * getGpuDeviceInfo() populates DeviceInfo with the ID and name
+ * of each GPU device.
+ *
+ * For example, getGpuDeviceInfor, might return:
+ * { { 0, <gpu-device-name> },
+ * { 1, <gpu-device-name> } }
+ *
+ * This information is used to identify GPU devices for GPU specific
+ * memory accounting (e.g. DMA buffer usage).
+ *
+ * @return vector of DeviceInfo populated for all GPU devices.
+ */
+ DeviceInfo[] getGpuDeviceInfo();
+}
diff --git a/memtrack/aidl/android/hardware/memtrack/MemtrackRecord.aidl b/memtrack/aidl/android/hardware/memtrack/MemtrackRecord.aidl
new file mode 100644
index 0000000..dae026e
--- /dev/null
+++ b/memtrack/aidl/android/hardware/memtrack/MemtrackRecord.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.memtrack;
+
+/*
+ * Each record consists of the size of the memory used by the process and
+ * flags indicate all the MemtrackFlags that are valid for this record.
+ */
+@VintfStability
+parcelable MemtrackRecord {
+ /* Memtrack Flags */
+ const int FLAG_SMAPS_ACCOUNTED = 1 << 1;
+ const int FLAG_SMAPS_UNACCOUNTED = 1 << 2;
+ const int FLAG_SHARED = 1 << 3;
+ const int FLAG_SHARED_PSS = 1 << 4;
+ const int FLAG_PRIVATE = 1 << 5;
+ const int FLAG_SYSTEM = 1 << 6;
+ const int FLAG_DEDICATED = 1 << 7;
+ const int FLAG_NONSECURE = 1 << 8;
+ const int FLAG_SECURE = 1 << 9;
+
+ /* Bitfield indicating all flags that are valid for this record */
+ int flags;
+
+ long sizeInBytes;
+}
+
diff --git a/keymint/aidl/android/hardware/keymint/EcCurve.aidl b/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
similarity index 75%
copy from keymint/aidl/android/hardware/keymint/EcCurve.aidl
copy to memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
index abd44b4..715c6bf 100644
--- a/keymint/aidl/android/hardware/keymint/EcCurve.aidl
+++ b/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.memtrack;
/**
- * Supported EC curves, used in ECDSA
+ * Tags which define the usage of the memory buffers.
*/
@VintfStability
@Backing(type="int")
-enum EcCurve {
- P_224 = 0,
- P_256 = 1,
- P_384 = 2,
- P_521 = 3,
+enum MemtrackType {
+ OTHER = 0,
+ GL = 1,
+ GRAPHICS = 2,
+ MULTIMEDIA = 3,
+ CAMERA = 4,
+ NUM_TYPES,
}
diff --git a/memtrack/aidl/default/Android.bp b/memtrack/aidl/default/Android.bp
new file mode 100644
index 0000000..52f88c8
--- /dev/null
+++ b/memtrack/aidl/default/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "android.hardware.memtrack-service.example",
+ relative_install_path: "hw",
+ init_rc: ["memtrack-default.rc"],
+ vintf_fragments: ["memtrack-default.xml"],
+ vendor: true,
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.memtrack-ndk_platform",
+ ],
+ srcs: [
+ "main.cpp",
+ "Memtrack.cpp",
+ ],
+}
diff --git a/memtrack/aidl/default/Memtrack.cpp b/memtrack/aidl/default/Memtrack.cpp
new file mode 100644
index 0000000..7361719
--- /dev/null
+++ b/memtrack/aidl/default/Memtrack.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#include "Memtrack.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+ndk::ScopedAStatus Memtrack::getMemory(int pid, MemtrackType type,
+ std::vector<MemtrackRecord>* _aidl_return) {
+ if (pid < 0) {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+ }
+ if (type < MemtrackType::OTHER || type >= MemtrackType::NUM_TYPES) {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+ }
+ _aidl_return->clear();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Memtrack::getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) {
+ _aidl_return->clear();
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/memtrack/aidl/default/Memtrack.h b/memtrack/aidl/default/Memtrack.h
new file mode 100644
index 0000000..f2ef60e
--- /dev/null
+++ b/memtrack/aidl/default/Memtrack.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/memtrack/BnMemtrack.h>
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+class Memtrack : public BnMemtrack {
+ ndk::ScopedAStatus getMemory(int pid, MemtrackType type,
+ std::vector<MemtrackRecord>* _aidl_return) override;
+
+ ndk::ScopedAStatus getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) override;
+};
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/memtrack/aidl/default/main.cpp b/memtrack/aidl/default/main.cpp
new file mode 100644
index 0000000..d063d2a
--- /dev/null
+++ b/memtrack/aidl/default/main.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#include "Memtrack.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::memtrack::Memtrack;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<Memtrack> memtrack = ndk::SharedRefBase::make<Memtrack>();
+
+ const std::string instance = std::string() + Memtrack::descriptor + "/default";
+ binder_status_t status =
+ AServiceManager_addService(memtrack->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // Unreachable
+}
diff --git a/memtrack/aidl/default/memtrack-default.rc b/memtrack/aidl/default/memtrack-default.rc
new file mode 100644
index 0000000..1725cd0
--- /dev/null
+++ b/memtrack/aidl/default/memtrack-default.rc
@@ -0,0 +1,4 @@
+service vendor.memtrack-default /vendor/bin/hw/android.hardware.memtrack-service.example
+ class hal
+ user nobody
+ group system
diff --git a/memtrack/aidl/default/memtrack-default.xml b/memtrack/aidl/default/memtrack-default.xml
new file mode 100644
index 0000000..3e3e0f6
--- /dev/null
+++ b/memtrack/aidl/default/memtrack-default.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.memtrack</name>
+ <fqname>IMemtrack/default</fqname>
+ </hal>
+</manifest>
+
diff --git a/memtrack/aidl/vts/Android.bp b/memtrack/aidl/vts/Android.bp
new file mode 100644
index 0000000..ea36677
--- /dev/null
+++ b/memtrack/aidl/vts/Android.bp
@@ -0,0 +1,17 @@
+cc_test {
+ name: "VtsHalMemtrackTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalMemtrackTargetTest.cpp"],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.memtrack-unstable-ndk_platform",
+ ],
+ test_suites: [
+ "vts-core",
+ ],
+}
diff --git a/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp b/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
new file mode 100644
index 0000000..4d33101
--- /dev/null
+++ b/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::memtrack::DeviceInfo;
+using aidl::android::hardware::memtrack::IMemtrack;
+using aidl::android::hardware::memtrack::MemtrackRecord;
+using aidl::android::hardware::memtrack::MemtrackType;
+
+class MemtrackAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ const auto instance = GetParam();
+ ASSERT_TRUE(AServiceManager_isDeclared(instance.c_str()));
+ auto memtrackBinder = ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()));
+ memtrack_ = IMemtrack::fromBinder(memtrackBinder);
+ ASSERT_NE(memtrack_, nullptr);
+ }
+
+ std::shared_ptr<IMemtrack> memtrack_;
+};
+
+TEST_P(MemtrackAidlTest, GetMemoryInvalidPid) {
+ int pid = -1;
+ MemtrackType type = MemtrackType::OTHER;
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_->getMemory(pid, type, &records);
+
+ EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+}
+
+TEST_P(MemtrackAidlTest, GetMemoryInvalidType) {
+ int pid = 1;
+ MemtrackType type = MemtrackType::NUM_TYPES;
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_->getMemory(pid, type, &records);
+
+ EXPECT_EQ(status.getExceptionCode(), EX_UNSUPPORTED_OPERATION);
+}
+
+TEST_P(MemtrackAidlTest, GetMemory) {
+ int pid = 1;
+ MemtrackType type = MemtrackType::OTHER;
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_->getMemory(pid, type, &records);
+
+ EXPECT_TRUE(status.isOk());
+}
+
+TEST_P(MemtrackAidlTest, GetGpuDeviceInfo) {
+ std::vector<DeviceInfo> device_info;
+
+ auto status = memtrack_->getGpuDeviceInfo(&device_info);
+
+ EXPECT_TRUE(status.isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemtrackAidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, MemtrackAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IMemtrack::descriptor)),
+ android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.0/utils/Android.bp b/neuralnetworks/1.0/utils/Android.bp
index 4d61fc0..d033617 100644
--- a/neuralnetworks/1.0/utils/Android.bp
+++ b/neuralnetworks/1.0/utils/Android.bp
@@ -32,3 +32,29 @@
"neuralnetworks_utils_hal_common",
],
}
+
+cc_test {
+ name: "neuralnetworks_utils_hal_1_0_test",
+ srcs: ["test/*.cpp"],
+ static_libs: [
+ "android.hardware.neuralnetworks@1.0",
+ "libgmock",
+ "libneuralnetworks_common",
+ "neuralnetworks_types",
+ "neuralnetworks_utils_hal_common",
+ "neuralnetworks_utils_hal_1_0",
+ ],
+ shared_libs: [
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libnativewindow",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
index 65b75e5..3b32e1d 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
@@ -27,8 +27,31 @@
#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_0::utils {
+// Converts the results of IDevice::getSupportedOperations* to the NN canonical format. On success,
+// this function returns with the supported operations as indicated by a driver. On failure, this
+// function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
+ ErrorStatus status, const hidl_vec<bool>& supportedOperations);
+
+// 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_OC_MR1. On failure, this function returns with the appropriate
+// nn::GeneralError.
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+ ErrorStatus status, const sp<IPreparedModel>& preparedModel);
+
+// Converts the results of IDevice::execute* to the NN canonical format. On success, this function
+// returns with an empty output shape vector and no timing information. On failure, this function
+// returns with the appropriate nn::ExecutionError.
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+ ErrorStatus status);
+
+// A HIDL callback class to receive the results of IDevice::prepareModel asynchronously.
class PreparedModelCallback final : public IPreparedModelCallback,
public hal::utils::IProtectedCallback {
public:
@@ -41,11 +64,10 @@
Data get();
private:
- void notifyInternal(Data result);
-
hal::utils::TransferValue<Data> mData;
};
+// A HIDL callback class to receive the results of IDevice::execute asynchronously.
class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
public:
using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
@@ -57,8 +79,6 @@
Data get();
private:
- void notifyInternal(Data result);
-
hal::utils::TransferValue<Data> mData;
};
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
index ee103ba..db3b2ad 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
@@ -32,8 +32,12 @@
#include <string>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_0::utils {
+// Class that adapts V1_0::IDevice to nn::IDevice.
class Device final : public nn::IDevice {
struct PrivateConstructorTag {};
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
index 31f366d..2de1828 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
@@ -29,8 +29,12 @@
#include <utility>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_0::utils {
+// Class that adapts V1_0::IPreparedModel to nn::IPreparedModel.
class PreparedModel final : public nn::IPreparedModel {
struct PrivateConstructorTag {};
@@ -44,13 +48,13 @@
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
const nn::Request& request, nn::MeasureTiming measure,
const nn::OptionalTimePoint& deadline,
- const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+ 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::OptionalTimeoutDuration& loopTimeoutDuration,
- const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+ const nn::OptionalDuration& loopTimeoutDuration,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
std::any getUnderlyingResource() const override;
diff --git a/neuralnetworks/1.0/utils/src/Callbacks.cpp b/neuralnetworks/1.0/utils/src/Callbacks.cpp
index b1259c3..ea3ea56 100644
--- a/neuralnetworks/1.0/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.0/utils/src/Callbacks.cpp
@@ -27,69 +27,62 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
#include <utility>
-namespace android::hardware::neuralnetworks::V1_0::utils {
-namespace {
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
-nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
- const sp<IPreparedModel>& preparedModel) {
- return NN_TRY(utils::PreparedModel::create(preparedModel));
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
+ ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
+ HANDLE_HAL_STATUS(status) << "get supported operations failed with " << toString(status);
+ return supportedOperations;
}
-} // namespace
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+ ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
+ HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ return NN_TRY(PreparedModel::create(preparedModel));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+ ErrorStatus status) {
+ HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+ return {};
+}
Return<void> PreparedModelCallback::notify(ErrorStatus status,
const sp<IPreparedModel>& preparedModel) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
- } else if (preparedModel == nullptr) {
- notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Returned preparedModel is nullptr");
- } else {
- notifyInternal(convertPreparedModel(preparedModel));
- }
+ mData.put(prepareModelCallback(status, preparedModel));
return Void();
}
void PreparedModelCallback::notifyAsDeadObject() {
- notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+ mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
}
PreparedModelCallback::Data PreparedModelCallback::get() {
return mData.take();
}
-void PreparedModelCallback::notifyInternal(PreparedModelCallback::Data result) {
- mData.put(std::move(result));
-}
-
// ExecutionCallback methods begin here
Return<void> ExecutionCallback::notify(ErrorStatus status) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
- } else {
- notifyInternal({});
- }
+ mData.put(executionCallback(status));
return Void();
}
void ExecutionCallback::notifyAsDeadObject() {
- notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+ mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
}
ExecutionCallback::Data ExecutionCallback::get() {
return mData.take();
}
-void ExecutionCallback::notifyInternal(ExecutionCallback::Data result) {
- mData.put(std::move(result));
-}
-
} // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
index 285c515..93bd81a 100644
--- a/neuralnetworks/1.0/utils/src/Device.cpp
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -31,6 +31,7 @@
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
#include <functional>
#include <memory>
@@ -38,27 +39,27 @@
#include <string>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_0::utils {
namespace {
-nn::GeneralResult<nn::Capabilities> initCapabilities(V1_0::IDevice* device) {
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
+ const Capabilities& capabilities) {
+ HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+ return nn::convert(capabilities);
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_0::IDevice* device) {
CHECK(device != nullptr);
- nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- const auto cb = [&result](ErrorStatus status, const Capabilities& capabilities) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "getCapabilities failed with " << toString(status);
- } else {
- result = nn::convert(capabilities);
- }
- };
+ auto cb = hal::utils::CallbackValue(capabilitiesCallback);
const auto ret = device->getCapabilities(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
} // namespace
@@ -74,7 +75,7 @@
<< "V1_0::utils::Device::create must have non-null device";
}
- auto capabilities = NN_TRY(initCapabilities(device.get()));
+ auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
return std::make_shared<const Device>(PrivateConstructorTag{}, std::move(name),
@@ -131,27 +132,12 @@
const auto hidlModel = NN_TRY(convert(modelInShared));
- nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- auto cb = [&result, &model](ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical)
- << "getSupportedOperations failed with " << toString(status);
- } else if (supportedOperations.size() != model.main.operations.size()) {
- result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "getSupportedOperations returned vector of size "
- << supportedOperations.size() << " but expected "
- << model.main.operations.size();
- } else {
- result = supportedOperations;
- }
- };
+ auto cb = hal::utils::CallbackValue(supportedOperationsCallback);
const auto ret = kDevice->getSupportedOperations(hidlModel, cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
@@ -170,10 +156,7 @@
const auto ret = kDevice->prepareModel(hidlModel, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "prepareModel failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
return cb->get();
}
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
index 46dd3f8..c0c22fb 100644
--- a/neuralnetworks/1.0/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -34,13 +34,15 @@
#include <utility>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_0::utils {
nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
sp<V1_0::IPreparedModel> preparedModel) {
if (preparedModel == nullptr) {
- return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
- << "V1_0::utils::PreparedModel::create must have non-null preparedModel";
+ return NN_ERROR() << "V1_0::utils::PreparedModel::create must have non-null preparedModel";
}
auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
@@ -55,7 +57,7 @@
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::OptionalTimeoutDuration& /*loopTimeoutDuration*/) const {
+ 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(
@@ -68,10 +70,7 @@
const auto ret = kPreparedModel->execute(hidlRequest, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "execute failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
auto result = NN_TRY(cb->get());
NN_TRY(hal::utils::makeExecutionFailure(
@@ -81,11 +80,12 @@
}
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::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
- const nn::OptionalTimeoutDuration& /*timeoutDurationAfterFence*/) const {
+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 {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
<< "IPreparedModel::executeFenced is not supported on 1.0 HAL service";
}
diff --git a/neuralnetworks/1.0/utils/test/DeviceTest.cpp b/neuralnetworks/1.0/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..e881da2
--- /dev/null
+++ b/neuralnetworks/1.0/utils/test/DeviceTest.cpp
@@ -0,0 +1,524 @@
+/*
+ * 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.
+ */
+
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+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 sp<V1_0::IDevice> kInvalidDevice;
+constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {
+ .execTime = std::numeric_limits<float>::max(),
+ .powerUsage = std::numeric_limits<float>::max()};
+
+template <typename... Args>
+auto makeCallbackReturn(Args&&... args) {
+ return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) {
+ std::apply(cb, argPack);
+ return Void();
+ };
+}
+
+sp<MockDevice> createMockDevice() {
+ const auto mockDevice = MockDevice::create();
+
+ // Setup default actions for each relevant call.
+ const auto getCapabilities_ret = makeCallbackReturn(
+ V1_0::ErrorStatus::NONE, V1_0::Capabilities{
+ .float32Performance = kNoPerformanceInfo,
+ .quantized8Performance = kNoPerformanceInfo,
+ });
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, getCapabilities(_)).WillByDefault(Invoke(getCapabilities_ret));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+auto makePreparedModelReturn(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus,
+ const sp<V1_0::utils::MockPreparedModel>& preparedModel) {
+ return [launchStatus, returnStatus, preparedModel](const V1_0::Model& /*model*/,
+ const sp<V1_0::IPreparedModelCallback>& cb)
+ -> hardware::Return<V1_0::ErrorStatus> {
+ cb->notify(returnStatus, preparedModel).isOk();
+ return launchStatus;
+ };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+ return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(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, getCapabilitiesError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_0::Capabilities{
+ .float32Performance = kNoPerformanceInfo,
+ .quantized8Performance = kNoPerformanceInfo,
+ });
+ EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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, linkToDeathError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = []() -> Return<bool> { return false; };
+ EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // 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, linkToDeathTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, linkToDeathRet())
+ .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, linkToDeathDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, linkToDeathRet())
+ .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_OC_MR1);
+}
+
+TEST(DeviceTest, getCachedData) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ 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, wait) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = []() -> Return<void> { return {}; };
+ EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(DeviceTest, waitTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, ping())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, waitDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto ret = [](const auto& model, const auto& cb) {
+ cb(V1_0::ErrorStatus::NONE, std::vector<bool>(model.operations.size(), true));
+ return hardware::Void();
+ };
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = [](const auto& /*model*/, const auto& cb) {
+ cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+ return hardware::Void();
+ };
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(1).WillOnce(Invoke(ret));
+
+ // 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 = V1_0::utils::MockPreparedModel::create();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+ V1_0::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(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_0::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(V1_0::ErrorStatus::NONE,
+ V1_0::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(V1_0::ErrorStatus::NONE,
+ V1_0::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 = [&mockDevice]() -> hardware::Return<V1_0::ErrorStatus> {
+ mockDevice->simulateCrash();
+ return V1_0::ErrorStatus::NONE;
+ };
+ 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, prepareModelFromCacheNotSupported) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // 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, allocateNotSupported) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/test/MockDevice.h b/neuralnetworks/1.0/utils/test/MockDevice.h
new file mode 100644
index 0000000..0fb59e3
--- /dev/null
+++ b/neuralnetworks/1.0/utils/test/MockDevice.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class MockDevice final : public IDevice {
+ public:
+ static sp<MockDevice> create();
+
+ // IBase methods below.
+ MOCK_METHOD(Return<void>, ping, (), (override));
+ MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+ Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t /*cookie*/);
+
+ // V1_0 methods below.
+ MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations,
+ (const V1_0::Model& model, getSupportedOperations_cb cb), (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel,
+ (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<V1_0::DeviceStatus>, getStatus, (), (override));
+
+ // Helper methods.
+ void simulateCrash();
+
+ private:
+ sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockDevice> MockDevice::create() {
+ auto mockDevice = sp<MockDevice>::make();
+
+ // Setup default actions for each relevant call.
+ const auto ret = []() -> Return<bool> { return true; };
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+inline Return<bool> MockDevice::linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) {
+ mDeathRecipient = recipient;
+ return linkToDeathRet();
+}
+
+inline void MockDevice::simulateCrash() {
+ ASSERT_NE(nullptr, mDeathRecipient.get());
+
+ // Currently, the utils::Device will not use the `cookie` or `who` arguments, so we pass in 0
+ // and nullptr for these arguments instead. Normally, they are used by the hidl_death_recipient
+ // to determine which object is dead. However, the utils::Device code only pairs a single death
+ // recipient with a single HIDL interface object, so these arguments are redundant.
+ mDeathRecipient->serviceDied(0, nullptr);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
diff --git a/neuralnetworks/1.0/utils/test/MockPreparedModel.h b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..7a48a83
--- /dev/null
+++ b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class MockPreparedModel final : public IPreparedModel {
+ public:
+ static sp<MockPreparedModel> create();
+
+ // IBase methods below.
+ MOCK_METHOD(Return<void>, ping, (), (override));
+ MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+ Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) override;
+
+ // V1_0 methods below.
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, execute,
+ (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback),
+ (override));
+
+ // Helper methods.
+ void simulateCrash();
+
+ private:
+ sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockPreparedModel> MockPreparedModel::create() {
+ auto mockPreparedModel = sp<MockPreparedModel>::make();
+
+ // Setup default actions for each relevant call.
+ const auto ret = []() -> Return<bool> { return true; };
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockPreparedModel, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(testing::AnyNumber());
+
+ return mockPreparedModel;
+}
+
+inline Return<bool> MockPreparedModel::linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) {
+ mDeathRecipient = recipient;
+ return linkToDeathRet();
+}
+
+inline void MockPreparedModel::simulateCrash() {
+ ASSERT_NE(nullptr, mDeathRecipient.get());
+
+ // Currently, the utils::PreparedModel will not use the `cookie` or `who` arguments, so we pass
+ // in 0 and nullptr for these arguments instead. Normally, they are used by the
+ // hidl_death_recipient to determine which object is dead. However, the utils::PreparedModel
+ // code only pairs a single death recipient with a single HIDL interface object, so these
+ // arguments are redundant.
+ mDeathRecipient->serviceDied(0, nullptr);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
diff --git a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
new file mode 100644
index 0000000..a5cbc72
--- /dev/null
+++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ */
+
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/PreparedModel.h>
+
+#include <functional>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const sp<V1_0::IPreparedModel> kInvalidPreparedModel;
+
+sp<MockPreparedModel> createMockPreparedModel() {
+ return MockPreparedModel::create();
+}
+
+auto makeExecute(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus) {
+ return [launchStatus, returnStatus](
+ const V1_0::Request& /*request*/,
+ const sp<V1_0::IExecutionCallback>& cb) -> Return<V1_0::ErrorStatus> {
+ cb->notify(returnStatus);
+ return launchStatus;
+ };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+ return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+} // 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, linkToDeathError) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto ret = []() -> Return<bool> { return false; };
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathTransportFailure) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathDeadObject) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, execute) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE)));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ EXPECT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeLaunchError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+ // 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, executeReturnError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+ // 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, executeTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .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, executeDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _))
+ .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, executeCrash) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+ const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+ mockPreparedModel->simulateCrash();
+ return V1_0::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // 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, executeFencedNotSupported) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+
+TEST(PreparedModelTest, getUnderlyingResource) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto resource = preparedModel->getUnderlyingResource();
+
+ // verify resource
+ const sp<V1_0::IPreparedModel>* maybeMock = std::any_cast<sp<V1_0::IPreparedModel>>(&resource);
+ ASSERT_NE(maybeMock, nullptr);
+ EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
+}
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.1/utils/Android.bp b/neuralnetworks/1.1/utils/Android.bp
index 909575b..fe0c80a 100644
--- a/neuralnetworks/1.1/utils/Android.bp
+++ b/neuralnetworks/1.1/utils/Android.bp
@@ -34,3 +34,31 @@
"neuralnetworks_utils_hal_common",
],
}
+
+cc_test {
+ name: "neuralnetworks_utils_hal_1_1_test",
+ srcs: ["test/*.cpp"],
+ static_libs: [
+ "android.hardware.neuralnetworks@1.0",
+ "android.hardware.neuralnetworks@1.1",
+ "libgmock",
+ "libneuralnetworks_common",
+ "neuralnetworks_types",
+ "neuralnetworks_utils_hal_common",
+ "neuralnetworks_utils_hal_1_0",
+ "neuralnetworks_utils_hal_1_1",
+ ],
+ shared_libs: [
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libnativewindow",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
index f646462..5d0769f 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
@@ -51,6 +51,10 @@
nn::GeneralResult<Model> convert(const nn::Model& model);
nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference);
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<V1_0::Request> convert(const nn::Request& request);
+nn::GeneralResult<V1_0::ErrorStatus> convert(const nn::ErrorStatus& status);
+
} // namespace android::hardware::neuralnetworks::V1_1::utils
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_CONVERSIONS_H
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
index c1e95fe1a..5e224b5 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
@@ -32,8 +32,12 @@
#include <string>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_1::utils {
+// Class that adapts V1_1::IDevice to nn::IDevice.
class Device final : public nn::IDevice {
struct PrivateConstructorTag {};
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index 359f68a..b47f25a 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -275,4 +275,16 @@
return validatedConvert(executionPreference);
}
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+ return V1_0::utils::convert(deviceStatus);
+}
+
+nn::GeneralResult<V1_0::Request> convert(const nn::Request& request) {
+ return V1_0::utils::convert(request);
+}
+
+nn::GeneralResult<V1_0::ErrorStatus> convert(const nn::ErrorStatus& status) {
+ return V1_0::utils::convert(status);
+}
+
} // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
index f73d3f8..3197ef4 100644
--- a/neuralnetworks/1.1/utils/src/Device.cpp
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -39,27 +39,27 @@
#include <string>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_1::utils {
namespace {
-nn::GeneralResult<nn::Capabilities> initCapabilities(V1_1::IDevice* device) {
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
+ const Capabilities& capabilities) {
+ HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+ return nn::convert(capabilities);
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_1::IDevice* device) {
CHECK(device != nullptr);
- nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- const auto cb = [&result](V1_0::ErrorStatus status, const Capabilities& capabilities) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "getCapabilities_1_1 failed with " << toString(status);
- } else {
- result = nn::convert(capabilities);
- }
- };
+ auto cb = hal::utils::CallbackValue(capabilitiesCallback);
const auto ret = device->getCapabilities_1_1(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
} // namespace
@@ -75,7 +75,7 @@
<< "V1_1::utils::Device::create must have non-null device";
}
- auto capabilities = NN_TRY(initCapabilities(device.get()));
+ auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
return std::make_shared<const Device>(PrivateConstructorTag{}, std::move(name),
@@ -132,28 +132,12 @@
const auto hidlModel = NN_TRY(convert(modelInShared));
- nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- auto cb = [&result, &model](V1_0::ErrorStatus status,
- const hidl_vec<bool>& supportedOperations) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical)
- << "getSupportedOperations_1_1 failed with " << toString(status);
- } else if (supportedOperations.size() != model.main.operations.size()) {
- result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "getSupportedOperations_1_1 returned vector of size "
- << supportedOperations.size() << " but expected "
- << model.main.operations.size();
- } else {
- result = supportedOperations;
- }
- };
+ auto cb = hal::utils::CallbackValue(V1_0::utils::supportedOperationsCallback);
const auto ret = kDevice->getSupportedOperations_1_1(hidlModel, cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
@@ -173,10 +157,7 @@
const auto ret = kDevice->prepareModel_1_1(hidlModel, hidlPreference, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "prepareModel failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
return cb->get();
}
diff --git a/neuralnetworks/1.1/utils/test/DeviceTest.cpp b/neuralnetworks/1.1/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..41e0e30
--- /dev/null
+++ b/neuralnetworks/1.1/utils/test/DeviceTest.cpp
@@ -0,0 +1,534 @@
+/*
+ * 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.
+ */
+
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.1/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+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 sp<V1_1::IDevice> kInvalidDevice;
+constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {
+ .execTime = std::numeric_limits<float>::max(),
+ .powerUsage = std::numeric_limits<float>::max()};
+
+template <typename... Args>
+auto makeCallbackReturn(Args&&... args) {
+ return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) {
+ std::apply(cb, argPack);
+ return Void();
+ };
+}
+
+sp<MockDevice> createMockDevice() {
+ const auto mockDevice = MockDevice::create();
+
+ // Setup default actions for each relevant call.
+ const auto getCapabilities_ret =
+ makeCallbackReturn(V1_0::ErrorStatus::NONE,
+ V1_1::Capabilities{
+ .float32Performance = kNoPerformanceInfo,
+ .quantized8Performance = kNoPerformanceInfo,
+ .relaxedFloat32toFloat16Performance = kNoPerformanceInfo,
+ });
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, getCapabilities_1_1(_)).WillByDefault(Invoke(getCapabilities_ret));
+
+ // Ensure that older calls are not used.
+ EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0);
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(0);
+ EXPECT_CALL(*mockDevice, prepareModel(_, _)).Times(0);
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+auto makePreparedModelReturn(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus,
+ const sp<V1_0::utils::MockPreparedModel>& preparedModel) {
+ return [launchStatus, returnStatus, preparedModel](const V1_1::Model& /*model*/,
+ V1_1::ExecutionPreference /*preference*/,
+ const sp<V1_0::IPreparedModelCallback>& cb)
+ -> hardware::Return<V1_0::ErrorStatus> {
+ cb->notify(returnStatus, preparedModel).isOk();
+ return launchStatus;
+ };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+ return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(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, getCapabilitiesError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret =
+ makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_1::Capabilities{
+ .float32Performance = kNoPerformanceInfo,
+ .quantized8Performance = kNoPerformanceInfo,
+ .relaxedFloat32toFloat16Performance = kNoPerformanceInfo,
+ });
+ EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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_1_1(_))
+ .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_1_1(_))
+ .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, linkToDeathError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = []() -> Return<bool> { return false; };
+ EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // 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, linkToDeathTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, linkToDeathRet())
+ .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, linkToDeathDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, linkToDeathRet())
+ .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_P);
+}
+
+TEST(DeviceTest, getCachedData) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ 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, wait) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = []() -> Return<void> { return {}; };
+ EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(DeviceTest, waitTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, ping())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, waitDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto ret = [](const auto& model, const auto& cb) {
+ cb(V1_0::ErrorStatus::NONE, std::vector<bool>(model.operations.size(), true));
+ return hardware::Void();
+ };
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = [](const auto& /*model*/, const auto& cb) {
+ cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+ return hardware::Void();
+ };
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _)).Times(1).WillOnce(Invoke(ret));
+
+ // 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_1_1(_, _))
+ .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_1_1(_, _))
+ .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 = V1_0::utils::MockPreparedModel::create();
+ EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+ V1_0::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_1_1(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_0::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_1_1(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+ V1_0::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_1_1(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+ V1_0::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_1_1(_, _, _))
+ .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_1_1(_, _, _))
+ .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 = [&mockDevice]() -> hardware::Return<V1_0::ErrorStatus> {
+ mockDevice->simulateCrash();
+ return V1_0::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _)).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, prepareModelFromCacheNotSupported) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // 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, allocateNotSupported) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/test/MockDevice.h b/neuralnetworks/1.1/utils/test/MockDevice.h
new file mode 100644
index 0000000..3b92e58
--- /dev/null
+++ b/neuralnetworks/1.1/utils/test/MockDevice.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
+
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+class MockDevice final : public IDevice {
+ public:
+ static sp<MockDevice> create();
+
+ // IBase methods below.
+ MOCK_METHOD(Return<void>, ping, (), (override));
+ MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+ Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t /*cookie*/);
+
+ // V1_0 methods below.
+ MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations,
+ (const V1_0::Model& model, getSupportedOperations_cb cb), (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel,
+ (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<V1_0::DeviceStatus>, getStatus, (), (override));
+
+ // V1_1 methods below.
+ MOCK_METHOD(Return<void>, getCapabilities_1_1, (getCapabilities_1_1_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations_1_1,
+ (const V1_1::Model& model, getSupportedOperations_1_1_cb cb), (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_1,
+ (const V1_1::Model& model, V1_1::ExecutionPreference preference,
+ const sp<V1_0::IPreparedModelCallback>& callback),
+ (override));
+
+ // Helper methods.
+ void simulateCrash();
+
+ private:
+ sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockDevice> MockDevice::create() {
+ auto mockDevice = sp<MockDevice>::make();
+
+ // Setup default actions for each relevant call.
+ const auto ret = []() -> Return<bool> { return true; };
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+inline Return<bool> MockDevice::linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) {
+ mDeathRecipient = recipient;
+ return linkToDeathRet();
+}
+
+inline void MockDevice::simulateCrash() {
+ ASSERT_NE(nullptr, mDeathRecipient.get());
+
+ // Currently, the utils::Device will not use the `cookie` or `who` arguments, so we pass in 0
+ // and nullptr for these arguments instead. Normally, they are used by the hidl_death_recipient
+ // to determine which object is dead. However, the utils::Device code only pairs a single death
+ // recipient with a single HIDL interface object, so these arguments are redundant.
+ mDeathRecipient->serviceDied(0, nullptr);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_1::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
diff --git a/neuralnetworks/1.1/utils/test/MockPreparedModel.h b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..aba731e
--- /dev/null
+++ b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class MockPreparedModel final : public IPreparedModel {
+ public:
+ static sp<MockPreparedModel> create();
+
+ // V1_0 methods below.
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, execute,
+ (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback),
+ (override));
+};
+
+inline sp<MockPreparedModel> MockPreparedModel::create() {
+ return sp<MockPreparedModel>::make();
+}
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
diff --git a/neuralnetworks/1.2/utils/Android.bp b/neuralnetworks/1.2/utils/Android.bp
index 22e8659..0fec41c 100644
--- a/neuralnetworks/1.2/utils/Android.bp
+++ b/neuralnetworks/1.2/utils/Android.bp
@@ -36,3 +36,33 @@
"neuralnetworks_utils_hal_common",
],
}
+
+cc_test {
+ name: "neuralnetworks_utils_hal_1_2_test",
+ srcs: ["test/*.cpp"],
+ static_libs: [
+ "android.hardware.neuralnetworks@1.0",
+ "android.hardware.neuralnetworks@1.1",
+ "android.hardware.neuralnetworks@1.2",
+ "libgmock",
+ "libneuralnetworks_common",
+ "neuralnetworks_types",
+ "neuralnetworks_utils_hal_common",
+ "neuralnetworks_utils_hal_1_0",
+ "neuralnetworks_utils_hal_1_1",
+ "neuralnetworks_utils_hal_1_2",
+ ],
+ shared_libs: [
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libnativewindow",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
index bc7d92a..ba3c1ba 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
@@ -31,8 +31,24 @@
#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_2::utils {
+// 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_Q. On failure, this function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+ V1_0::ErrorStatus status, const sp<IPreparedModel>& preparedModel);
+
+// Converts the results of IDevice::execute* to the NN canonical format. On success, this function
+// returns with the output shapes and the timing information. On failure, this function returns with
+// the appropriate nn::ExecutionError.
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+ V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes, const Timing& timing);
+
+// A HIDL callback class to receive the results of IDevice::prepareModel* asynchronously.
class PreparedModelCallback final : public IPreparedModelCallback,
public hal::utils::IProtectedCallback {
public:
@@ -48,11 +64,10 @@
Data get();
private:
- void notifyInternal(Data result);
-
hal::utils::TransferValue<Data> mData;
};
+// A HIDL callback class to receive the results of IDevice::execute_1_2 asynchronously.
class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
public:
using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
@@ -66,8 +81,6 @@
Data get();
private:
- void notifyInternal(Data result);
-
hal::utils::TransferValue<Data> mData;
};
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 5dcbc0b..6fd1337 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
@@ -97,6 +97,12 @@
nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles);
nn::GeneralResult<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes);
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<V1_0::Request> convert(const nn::Request& request);
+nn::GeneralResult<V1_0::ErrorStatus> convert(const nn::ErrorStatus& status);
+nn::GeneralResult<V1_1::ExecutionPreference> convert(
+ const nn::ExecutionPreference& executionPreference);
+
} // namespace android::hardware::neuralnetworks::V1_2::utils
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_CONVERSIONS_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
index a68830d..b4bef5e 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
@@ -32,14 +32,29 @@
#include <string>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_2::utils {
-nn::GeneralResult<std::string> initVersionString(V1_2::IDevice* device);
-nn::GeneralResult<nn::DeviceType> initDeviceType(V1_2::IDevice* device);
-nn::GeneralResult<std::vector<nn::Extension>> initExtensions(V1_2::IDevice* device);
-nn::GeneralResult<std::pair<uint32_t, uint32_t>> initNumberOfCacheFilesNeeded(
+// Retrieves the version string from the provided device object. On failure, this function returns
+// with the appropriate nn::GeneralError.
+nn::GeneralResult<std::string> getVersionStringFrom(V1_2::IDevice* device);
+
+// Retrieves the device type from the provided device object. On failure, this function returns with
+// the appropriate nn::GeneralError.
+nn::GeneralResult<nn::DeviceType> getDeviceTypeFrom(V1_2::IDevice* device);
+
+// Retrieves the extensions supported by the provided device object. On failure, this function
+// returns with the appropriate nn::GeneralError.
+nn::GeneralResult<std::vector<nn::Extension>> getSupportedExtensionsFrom(V1_2::IDevice* device);
+
+// Retrieves the number of model cache files and data cache files needed by the provided device
+// object. On failure, this function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom(
V1_2::IDevice* device);
+// Class that adapts V1_2::IDevice to nn::IDevice.
class Device final : public nn::IDevice {
struct PrivateConstructorTag {};
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
index 65e1e8a..6a56a82 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
@@ -30,28 +30,32 @@
#include <utility>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_2::utils {
+// Class that adapts V1_2::IPreparedModel to nn::IPreparedModel.
class PreparedModel final : public nn::IPreparedModel {
struct PrivateConstructorTag {};
public:
static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
- sp<V1_2::IPreparedModel> preparedModel);
+ sp<V1_2::IPreparedModel> preparedModel, bool executeSynchronously);
- PreparedModel(PrivateConstructorTag tag, sp<V1_2::IPreparedModel> preparedModel,
- hal::utils::DeathHandler deathHandler);
+ PreparedModel(PrivateConstructorTag tag, bool executeSynchronously,
+ sp<V1_2::IPreparedModel> preparedModel, hal::utils::DeathHandler deathHandler);
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
const nn::Request& request, nn::MeasureTiming measure,
const nn::OptionalTimePoint& deadline,
- const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+ 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::OptionalTimeoutDuration& loopTimeoutDuration,
- const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+ const nn::OptionalDuration& loopTimeoutDuration,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
std::any getUnderlyingResource() const override;
@@ -61,6 +65,7 @@
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeAsynchronously(
const V1_0::Request& request, MeasureTiming measure) const;
+ const bool kExecuteSynchronously;
const sp<V1_2::IPreparedModel> kPreparedModel;
const hal::utils::DeathHandler kDeathHandler;
};
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
index 70149a2..c289fc8 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -30,6 +30,8 @@
namespace android::hardware::neuralnetworks::V1_2::utils {
+using CacheToken = hidl_array<uint8_t, static_cast<size_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+
constexpr auto kDefaultMesaureTiming = MeasureTiming::NO;
constexpr auto kNoTiming = Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
.timeInDriver = std::numeric_limits<uint64_t>::max()};
diff --git a/neuralnetworks/1.2/utils/src/Callbacks.cpp b/neuralnetworks/1.2/utils/src/Callbacks.cpp
index 39f88c2..fefa122 100644
--- a/neuralnetworks/1.2/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.2/utils/src/Callbacks.cpp
@@ -27,6 +27,7 @@
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.0/PreparedModel.h>
#include <nnapi/hal/CommonUtils.h>
@@ -36,107 +37,79 @@
#include <utility>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_2::utils {
namespace {
-nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
- const sp<V1_0::IPreparedModel>& preparedModel) {
- return NN_TRY(V1_0::utils::PreparedModel::create(preparedModel));
-}
-
-nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
- const sp<IPreparedModel>& preparedModel) {
- return NN_TRY(utils::PreparedModel::create(preparedModel));
-}
-
nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
convertExecutionGeneralResultsHelper(const hidl_vec<OutputShape>& outputShapes,
const Timing& timing) {
return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
}
-nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
-convertExecutionGeneralResults(const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+} // namespace
+
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+ V1_0::ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
+ HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ return NN_TRY(PreparedModel::create(preparedModel, /*executeSynchronously=*/true));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+ V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+ if (status == V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+ auto canonicalOutputShapes =
+ nn::convert(outputShapes).value_or(std::vector<nn::OutputShape>{});
+ return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
+ << "execution failed with " << toString(status);
+ }
+ HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
return hal::utils::makeExecutionFailure(
convertExecutionGeneralResultsHelper(outputShapes, timing));
}
-} // namespace
-
Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
const sp<V1_0::IPreparedModel>& preparedModel) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
- } else if (preparedModel == nullptr) {
- notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Returned preparedModel is nullptr");
- } else {
- notifyInternal(convertPreparedModel(preparedModel));
- }
+ mData.put(V1_0::utils::prepareModelCallback(status, preparedModel));
return Void();
}
Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
const sp<IPreparedModel>& preparedModel) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
- } else if (preparedModel == nullptr) {
- notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Returned preparedModel is nullptr");
- } else {
- notifyInternal(convertPreparedModel(preparedModel));
- }
+ mData.put(prepareModelCallback(status, preparedModel));
return Void();
}
void PreparedModelCallback::notifyAsDeadObject() {
- notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+ mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
}
PreparedModelCallback::Data PreparedModelCallback::get() {
return mData.take();
}
-void PreparedModelCallback::notifyInternal(PreparedModelCallback::Data result) {
- mData.put(std::move(result));
-}
-
// ExecutionCallback methods begin here
Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
- } else {
- notifyInternal({});
- }
+ mData.put(V1_0::utils::executionCallback(status));
return Void();
}
Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus status,
const hidl_vec<OutputShape>& outputShapes,
const Timing& timing) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
- } else {
- notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
- }
+ mData.put(executionCallback(status, outputShapes, timing));
return Void();
}
void ExecutionCallback::notifyAsDeadObject() {
- notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+ mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
}
ExecutionCallback::Data ExecutionCallback::get() {
return mData.take();
}
-void ExecutionCallback::notifyInternal(ExecutionCallback::Data result) {
- mData.put(std::move(result));
-}
-
} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index f11474f..062f6f7 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -26,6 +26,7 @@
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.1/Conversions.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
@@ -43,7 +44,9 @@
return static_cast<std::underlying_type_t<Type>>(value);
}
+using HalDuration = std::chrono::duration<uint64_t, std::micro>;
constexpr auto kVersion = android::nn::Version::ANDROID_Q;
+constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
} // namespace
@@ -270,7 +273,18 @@
}
GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing) {
- return Timing{.timeOnDevice = timing.timeOnDevice, .timeInDriver = timing.timeInDriver};
+ constexpr uint64_t kMaxTiming = std::chrono::floor<HalDuration>(Duration::max()).count();
+ constexpr auto convertTiming = [](uint64_t halTiming) -> OptionalDuration {
+ if (halTiming == kNoTiming) {
+ return {};
+ }
+ if (halTiming > kMaxTiming) {
+ return Duration::max();
+ }
+ return HalDuration{halTiming};
+ };
+ return Timing{.timeOnDevice = convertTiming(timing.timeOnDevice),
+ .timeInDriver = convertTiming(timing.timeInDriver)};
}
GeneralResult<Extension> unvalidatedConvert(const hal::V1_2::Extension& extension) {
@@ -547,7 +561,14 @@
}
nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
- return Timing{.timeOnDevice = timing.timeOnDevice, .timeInDriver = timing.timeInDriver};
+ constexpr auto convertTiming = [](nn::OptionalDuration canonicalTiming) -> uint64_t {
+ if (!canonicalTiming.has_value()) {
+ return kNoTiming;
+ }
+ return std::chrono::ceil<HalDuration>(*canonicalTiming).count();
+ };
+ return Timing{.timeOnDevice = convertTiming(timing.timeOnDevice),
+ .timeInDriver = convertTiming(timing.timeInDriver)};
}
nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension) {
@@ -602,4 +623,21 @@
return validatedConvert(outputShapes);
}
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+ return V1_1::utils::convert(deviceStatus);
+}
+
+nn::GeneralResult<V1_0::Request> convert(const nn::Request& request) {
+ return V1_1::utils::convert(request);
+}
+
+nn::GeneralResult<V1_0::ErrorStatus> convert(const nn::ErrorStatus& status) {
+ return V1_1::utils::convert(status);
+}
+
+nn::GeneralResult<V1_1::ExecutionPreference> convert(
+ const nn::ExecutionPreference& executionPreference) {
+ return V1_1::utils::convert(executionPreference);
+}
+
} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
index 0061065..9fe0de2 100644
--- a/neuralnetworks/1.2/utils/src/Device.cpp
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -41,112 +41,108 @@
#include <string>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_2::utils {
namespace {
-nn::GeneralResult<nn::Capabilities> initCapabilities(V1_2::IDevice* device) {
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
+ const Capabilities& capabilities) {
+ HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+ return nn::convert(capabilities);
+}
+
+nn::GeneralResult<std::string> versionStringCallback(V1_0::ErrorStatus status,
+ const hidl_string& versionString) {
+ HANDLE_HAL_STATUS(status) << "getVersionString failed with " << toString(status);
+ return versionString;
+}
+
+nn::GeneralResult<nn::DeviceType> deviceTypeCallback(V1_0::ErrorStatus status,
+ DeviceType deviceType) {
+ HANDLE_HAL_STATUS(status) << "getDeviceType failed with " << toString(status);
+ return nn::convert(deviceType);
+}
+
+nn::GeneralResult<std::vector<nn::Extension>> supportedExtensionsCallback(
+ V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
+ HANDLE_HAL_STATUS(status) << "getExtensions failed with " << toString(status);
+ return nn::convert(extensions);
+}
+
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> numberOfCacheFilesNeededCallback(
+ V1_0::ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
+ HANDLE_HAL_STATUS(status) << "getNumberOfCacheFilesNeeded failed with " << toString(status);
+ if (numModelCache > nn::kMaxNumberOfCacheFiles) {
+ return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numModelCache files greater "
+ "than allowed max ("
+ << numModelCache << " vs " << nn::kMaxNumberOfCacheFiles << ")";
+ }
+ if (numDataCache > nn::kMaxNumberOfCacheFiles) {
+ return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numDataCache files greater "
+ "than allowed max ("
+ << numDataCache << " vs " << nn::kMaxNumberOfCacheFiles << ")";
+ }
+ return std::make_pair(numModelCache, numDataCache);
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_2::IDevice* device) {
CHECK(device != nullptr);
- nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- const auto cb = [&result](V1_0::ErrorStatus status, const Capabilities& capabilities) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "getCapabilities_1_2 failed with " << toString(status);
- } else {
- result = nn::convert(capabilities);
- }
- };
+ auto cb = hal::utils::CallbackValue(capabilitiesCallback);
const auto ret = device->getCapabilities_1_2(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
} // namespace
-nn::GeneralResult<std::string> initVersionString(V1_2::IDevice* device) {
+nn::GeneralResult<std::string> getVersionStringFrom(V1_2::IDevice* device) {
CHECK(device != nullptr);
- nn::GeneralResult<std::string> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- const auto cb = [&result](V1_0::ErrorStatus status, const hidl_string& versionString) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "getVersionString failed with " << toString(status);
- } else {
- result = versionString;
- }
- };
+ auto cb = hal::utils::CallbackValue(versionStringCallback);
const auto ret = device->getVersionString(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
-nn::GeneralResult<nn::DeviceType> initDeviceType(V1_2::IDevice* device) {
+nn::GeneralResult<nn::DeviceType> getDeviceTypeFrom(V1_2::IDevice* device) {
CHECK(device != nullptr);
- nn::GeneralResult<nn::DeviceType> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- const auto cb = [&result](V1_0::ErrorStatus status, DeviceType deviceType) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "getDeviceType failed with " << toString(status);
- } else {
- result = nn::convert(deviceType);
- }
- };
+ auto cb = hal::utils::CallbackValue(deviceTypeCallback);
const auto ret = device->getType(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
-nn::GeneralResult<std::vector<nn::Extension>> initExtensions(V1_2::IDevice* device) {
+nn::GeneralResult<std::vector<nn::Extension>> getSupportedExtensionsFrom(V1_2::IDevice* device) {
CHECK(device != nullptr);
- nn::GeneralResult<std::vector<nn::Extension>> result =
- NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
- const auto cb = [&result](V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "getExtensions failed with " << toString(status);
- } else {
- result = nn::convert(extensions);
- }
- };
+ auto cb = hal::utils::CallbackValue(supportedExtensionsCallback);
const auto ret = device->getSupportedExtensions(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
-nn::GeneralResult<std::pair<uint32_t, uint32_t>> initNumberOfCacheFilesNeeded(
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom(
V1_2::IDevice* device) {
CHECK(device != nullptr);
- nn::GeneralResult<std::pair<uint32_t, uint32_t>> result =
- NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
- const auto cb = [&result](V1_0::ErrorStatus status, uint32_t numModelCache,
- uint32_t numDataCache) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical)
- << "getNumberOfCacheFilesNeeded failed with " << toString(status);
- } else {
- result = std::make_pair(numModelCache, numDataCache);
- }
- };
+ auto cb = hal::utils::CallbackValue(numberOfCacheFilesNeededCallback);
const auto ret = device->getNumberOfCacheFilesNeeded(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
@@ -160,11 +156,11 @@
<< "V1_2::utils::Device::create must have non-null device";
}
- auto versionString = NN_TRY(initVersionString(device.get()));
- const auto deviceType = NN_TRY(initDeviceType(device.get()));
- auto extensions = NN_TRY(initExtensions(device.get()));
- auto capabilities = NN_TRY(initCapabilities(device.get()));
- const auto numberOfCacheFilesNeeded = NN_TRY(initNumberOfCacheFilesNeeded(device.get()));
+ 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(hal::utils::DeathHandler::create(device));
return std::make_shared<const Device>(
@@ -229,28 +225,12 @@
const auto hidlModel = NN_TRY(convert(modelInShared));
- nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- auto cb = [&result, &model](V1_0::ErrorStatus status,
- const hidl_vec<bool>& supportedOperations) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical)
- << "getSupportedOperations_1_2 failed with " << toString(status);
- } else if (supportedOperations.size() != model.main.operations.size()) {
- result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "getSupportedOperations_1_2 returned vector of size "
- << supportedOperations.size() << " but expected "
- << model.main.operations.size();
- } else {
- result = supportedOperations;
- }
- };
+ auto cb = hal::utils::CallbackValue(V1_0::utils::supportedOperationsCallback);
const auto ret = kDevice->getSupportedOperations_1_2(hidlModel, cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
@@ -263,10 +243,10 @@
NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
const auto hidlModel = NN_TRY(convert(modelInShared));
- const auto hidlPreference = NN_TRY(V1_1::utils::convert(preference));
+ const auto hidlPreference = NN_TRY(convert(preference));
const auto hidlModelCache = NN_TRY(convert(modelCache));
const auto hidlDataCache = NN_TRY(convert(dataCache));
- const auto hidlToken = token;
+ const auto hidlToken = CacheToken{token};
const auto cb = sp<PreparedModelCallback>::make();
const auto scoped = kDeathHandler.protectCallback(cb.get());
@@ -274,10 +254,7 @@
const auto ret = kDevice->prepareModel_1_2(hidlModel, hidlPreference, hidlModelCache,
hidlDataCache, hidlToken, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "prepareModel_1_2 failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
return cb->get();
}
@@ -287,17 +264,14 @@
const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
const auto hidlModelCache = NN_TRY(convert(modelCache));
const auto hidlDataCache = NN_TRY(convert(dataCache));
- const auto hidlToken = token;
+ const auto hidlToken = CacheToken{token};
const auto cb = sp<PreparedModelCallback>::make();
const auto scoped = kDeathHandler.protectCallback(cb.get());
const auto ret = kDevice->prepareModelFromCache(hidlModelCache, hidlDataCache, hidlToken, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "prepareModelFromCache failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
return cb->get();
}
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index dad9a7e..6d00082 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -37,55 +37,37 @@
#include <utility>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_2::utils {
-namespace {
-
-nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
-convertExecutionResultsHelper(const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
- return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
-}
-
-nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
- const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
- return hal::utils::makeExecutionFailure(convertExecutionResultsHelper(outputShapes, timing));
-}
-
-} // namespace
nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
- sp<V1_2::IPreparedModel> preparedModel) {
+ sp<V1_2::IPreparedModel> preparedModel, bool executeSynchronously) {
if (preparedModel == nullptr) {
- return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
- << "V1_2::utils::PreparedModel::create must have non-null preparedModel";
+ return NN_ERROR() << "V1_2::utils::PreparedModel::create must have non-null preparedModel";
}
auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
- return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
- std::move(deathHandler));
+ return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, executeSynchronously,
+ std::move(preparedModel), std::move(deathHandler));
}
-PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_2::IPreparedModel> preparedModel,
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, bool executeSynchronously,
+ sp<V1_2::IPreparedModel> preparedModel,
hal::utils::DeathHandler deathHandler)
- : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
+ : kExecuteSynchronously(executeSynchronously),
+ kPreparedModel(std::move(preparedModel)),
+ kDeathHandler(std::move(deathHandler)) {}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
PreparedModel::executeSynchronously(const V1_0::Request& request, MeasureTiming measure) const {
- nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
- NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
- const auto cb = [&result](V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
- const Timing& timing) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
- } else {
- result = convertExecutionResults(outputShapes, timing);
- }
- };
+ auto cb = hal::utils::CallbackValue(executionCallback);
const auto ret = kPreparedModel->executeSynchronously(request, measure, cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
@@ -95,9 +77,8 @@
const auto ret = kPreparedModel->execute_1_2(request, measure, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "execute failed with " << toString(status);
+ if (status != V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+ HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
}
return cb->get();
@@ -106,51 +87,38 @@
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::OptionalTimeoutDuration& /*loopTimeoutDuration*/) const {
+ 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 hidlRequest =
- NN_TRY(hal::utils::makeExecutionFailure(V1_0::utils::convert(requestInShared)));
+ const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
- nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
- NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
- const bool preferSynchronous = true;
+ auto result = kExecuteSynchronously ? executeSynchronously(hidlRequest, hidlMeasure)
+ : executeAsynchronously(hidlRequest, hidlMeasure);
+ auto [outputShapes, timing] = NN_TRY(std::move(result));
- // Execute synchronously if allowed.
- if (preferSynchronous) {
- result = executeSynchronously(hidlRequest, hidlMeasure);
- }
+ NN_TRY(hal::utils::makeExecutionFailure(
+ hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
- // Run asymchronous execution if execution has not already completed.
- if (!result.has_value()) {
- result = executeAsynchronously(hidlRequest, hidlMeasure);
- }
-
- // Flush output buffers if suxcessful execution.
- if (result.has_value()) {
- NN_TRY(hal::utils::makeExecutionFailure(
- hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
- }
-
- return result;
+ 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::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
- const nn::OptionalTimeoutDuration& /*timeoutDurationAfterFence*/) const {
+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 {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
<< "IPreparedModel::executeFenced is not supported on 1.2 HAL service";
}
std::any PreparedModel::getUnderlyingResource() const {
- sp<V1_0::IPreparedModel> resource = kPreparedModel;
+ sp<V1_2::IPreparedModel> resource = kPreparedModel;
return resource;
}
diff --git a/neuralnetworks/1.2/utils/test/DeviceTest.cpp b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..9c8adde
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
@@ -0,0 +1,875 @@
+/*
+ * 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.
+ */
+
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.2/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+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 sp<V1_2::IDevice> kInvalidDevice;
+constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {
+ .execTime = std::numeric_limits<float>::max(),
+ .powerUsage = std::numeric_limits<float>::max()};
+
+template <typename... Args>
+auto makeCallbackReturn(Args&&... args) {
+ return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) {
+ std::apply(cb, argPack);
+ return Void();
+ };
+}
+
+sp<MockDevice> createMockDevice() {
+ const auto mockDevice = MockDevice::create();
+
+ // Setup default actions for each relevant call.
+ const auto getVersionString_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, kName);
+ const auto getType_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, V1_2::DeviceType::OTHER);
+ const auto getSupportedExtensions_ret =
+ makeCallbackReturn(V1_0::ErrorStatus::NONE, hidl_vec<V1_2::Extension>{});
+ const auto getNumberOfCacheFilesNeeded_ret = makeCallbackReturn(
+ V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles, nn::kMaxNumberOfCacheFiles);
+ const auto getCapabilities_ret = makeCallbackReturn(
+ V1_0::ErrorStatus::NONE,
+ V1_2::Capabilities{
+ .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+ .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+ });
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, getVersionString(_)).WillByDefault(Invoke(getVersionString_ret));
+ ON_CALL(*mockDevice, getType(_)).WillByDefault(Invoke(getType_ret));
+ ON_CALL(*mockDevice, getSupportedExtensions(_))
+ .WillByDefault(Invoke(getSupportedExtensions_ret));
+ ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+ .WillByDefault(Invoke(getNumberOfCacheFilesNeeded_ret));
+ ON_CALL(*mockDevice, getCapabilities_1_2(_)).WillByDefault(Invoke(getCapabilities_ret));
+
+ // Ensure that older calls are not used.
+ EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0);
+ EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(0);
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(0);
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _)).Times(0);
+ EXPECT_CALL(*mockDevice, prepareModel(_, _)).Times(0);
+ EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _)).Times(0);
+
+ // 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_1_2(_)).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+auto makePreparedModelReturn(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus,
+ const sp<MockPreparedModel>& preparedModel) {
+ return [launchStatus, returnStatus, preparedModel](
+ const V1_2::Model& /*model*/, V1_1::ExecutionPreference /*preference*/,
+ const hardware::hidl_vec<hardware::hidl_handle>& /*modelCache*/,
+ const hardware::hidl_vec<hardware::hidl_handle>& /*dataCache*/,
+ const CacheToken& /*token*/, const sp<V1_2::IPreparedModelCallback>& cb)
+ -> hardware::Return<V1_0::ErrorStatus> {
+ cb->notify_1_2(returnStatus, preparedModel).isOk();
+ return launchStatus;
+ };
+}
+auto makePreparedModelFromCacheReturn(V1_0::ErrorStatus launchStatus,
+ V1_0::ErrorStatus returnStatus,
+ const sp<MockPreparedModel>& preparedModel) {
+ return [launchStatus, returnStatus, preparedModel](
+ const hardware::hidl_vec<hardware::hidl_handle>& /*modelCache*/,
+ const hardware::hidl_vec<hardware::hidl_handle>& /*dataCache*/,
+ const CacheToken& /*token*/, const sp<V1_2::IPreparedModelCallback>& cb)
+ -> hardware::Return<V1_0::ErrorStatus> {
+ cb->notify_1_2(returnStatus, preparedModel).isOk();
+ return launchStatus;
+ };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+ return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(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();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, "");
+ EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret =
+ makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_2::DeviceType::OTHER);
+ EXPECT_CALL(*mockDevice, getType(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret =
+ makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, hidl_vec<V1_2::Extension>{});
+ EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+ nn::kMaxNumberOfCacheFiles, nn::kMaxNumberOfCacheFiles);
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles + 1,
+ nn::kMaxNumberOfCacheFiles);
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles,
+ nn::kMaxNumberOfCacheFiles + 1);
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = makeCallbackReturn(
+ V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_2::Capabilities{
+ .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+ .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+ });
+ EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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_1_2(_))
+ .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_1_2(_))
+ .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, linkToDeathError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = []() -> Return<bool> { return false; };
+ EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // 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, linkToDeathTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, linkToDeathRet())
+ .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, linkToDeathDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, linkToDeathRet())
+ .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_Q);
+}
+
+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_1_2(_)).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, wait) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = []() -> Return<void> { return {}; };
+ EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(DeviceTest, waitTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, ping())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, waitDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto ret = [](const auto& model, const auto& cb) {
+ cb(V1_0::ErrorStatus::NONE, std::vector<bool>(model.operations.size(), true));
+ return hardware::Void();
+ };
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_2(_, _)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = [](const auto& /*model*/, const auto& cb) {
+ cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+ return hardware::Void();
+ };
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_2(_, _)).Times(1).WillOnce(Invoke(ret));
+
+ // 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_1_2(_, _))
+ .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_1_2(_, _))
+ .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_1_2(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+ V1_0::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_1_2(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_0::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_1_2(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+ V1_0::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_1_2(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+ V1_0::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_1_2(_, _, _, _, _, _))
+ .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_1_2(_, _, _, _, _, _))
+ .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 = [&mockDevice]() -> hardware::Return<V1_0::ErrorStatus> {
+ mockDevice->simulateCrash();
+ return V1_0::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _))
+ .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(
+ V1_0::ErrorStatus::NONE, V1_0::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, prepareModelFromCacheError) {
+ // 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::GENERAL_FAILURE,
+ 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();
+ const auto device = Device::create(kName, mockDevice).value();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelFromCacheReturn(V1_0::ErrorStatus::NONE,
+ V1_0::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 = [&mockDevice]() -> hardware::Return<V1_0::ErrorStatus> {
+ mockDevice->simulateCrash();
+ return V1_0::ErrorStatus::NONE;
+ };
+ 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, allocateNotSupported) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/test/MockDevice.h b/neuralnetworks/1.2/utils/test/MockDevice.h
new file mode 100644
index 0000000..b459943
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/MockDevice.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
+
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+using CacheToken =
+ hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+
+class MockDevice final : public IDevice {
+ public:
+ static sp<MockDevice> create();
+
+ // IBase methods below.
+ MOCK_METHOD(Return<void>, ping, (), (override));
+ MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+ Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t /*cookie*/);
+
+ // V1_0 methods below.
+ MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations,
+ (const V1_0::Model& model, getSupportedOperations_cb cb), (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel,
+ (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<V1_0::DeviceStatus>, getStatus, (), (override));
+
+ // V1_1 methods below.
+ MOCK_METHOD(Return<void>, getCapabilities_1_1, (getCapabilities_1_1_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations_1_1,
+ (const V1_1::Model& model, getSupportedOperations_1_1_cb cb), (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_1,
+ (const V1_1::Model& model, V1_1::ExecutionPreference preference,
+ const sp<V1_0::IPreparedModelCallback>& callback),
+ (override));
+
+ // V1_2 methods below.
+ MOCK_METHOD(Return<void>, getVersionString, (getVersionString_cb cb), (override));
+ MOCK_METHOD(Return<void>, getType, (getType_cb cb), (override));
+ MOCK_METHOD(Return<void>, getCapabilities_1_2, (getCapabilities_1_2_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedExtensions, (getSupportedExtensions_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations_1_2,
+ (const V1_2::Model& model, getSupportedOperations_1_2_cb cb), (override));
+ MOCK_METHOD(Return<void>, getNumberOfCacheFilesNeeded, (getNumberOfCacheFilesNeeded_cb cb),
+ (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_2,
+ (const V1_2::Model& model, V1_1::ExecutionPreference preference,
+ const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+ const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModelFromCache,
+ (const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+ const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback),
+ (override));
+
+ // Helper methods.
+ void simulateCrash();
+
+ private:
+ sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockDevice> MockDevice::create() {
+ auto mockDevice = sp<MockDevice>::make();
+
+ // Setup default actions for each relevant call.
+ const auto ret = []() -> Return<bool> { return true; };
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+inline Return<bool> MockDevice::linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) {
+ mDeathRecipient = recipient;
+ return linkToDeathRet();
+}
+
+inline void MockDevice::simulateCrash() {
+ ASSERT_NE(nullptr, mDeathRecipient.get());
+
+ // Currently, the utils::Device will not use the `cookie` or `who` arguments, so we pass in 0
+ // and nullptr for these arguments instead. Normally, they are used by the hidl_death_recipient
+ // to determine which object is dead. However, the utils::Device code only pairs a single death
+ // recipient with a single HIDL interface object, so these arguments are redundant.
+ mDeathRecipient->serviceDied(0, nullptr);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
diff --git a/neuralnetworks/1.2/utils/test/MockPreparedModel.h b/neuralnetworks/1.2/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..f5fd1f3
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/MockPreparedModel.h
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
+
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class MockPreparedModel final : public IPreparedModel {
+ public:
+ static sp<MockPreparedModel> create();
+
+ // IBase methods below.
+ MOCK_METHOD(Return<void>, ping, (), (override));
+ MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+ Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) override;
+
+ // V1_0 methods below.
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, execute,
+ (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback),
+ (override));
+
+ // V1_2 methods below.
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, execute_1_2,
+ (const V1_0::Request& request, V1_2::MeasureTiming measure,
+ const sp<V1_2::IExecutionCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<void>, executeSynchronously,
+ (const V1_0::Request& request, V1_2::MeasureTiming measure,
+ executeSynchronously_cb cb),
+ (override));
+ MOCK_METHOD(Return<void>, configureExecutionBurst,
+ (const sp<V1_2::IBurstCallback>& callback,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
+ configureExecutionBurst_cb cb),
+ (override));
+
+ // Helper methods.
+ void simulateCrash();
+
+ private:
+ sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockPreparedModel> MockPreparedModel::create() {
+ auto mockPreparedModel = sp<MockPreparedModel>::make();
+
+ // Setup default actions for each relevant call.
+ const auto ret = []() -> Return<bool> { return true; };
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockPreparedModel, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(testing::AnyNumber());
+
+ return mockPreparedModel;
+}
+
+inline Return<bool> MockPreparedModel::linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) {
+ mDeathRecipient = recipient;
+ return linkToDeathRet();
+}
+
+inline void MockPreparedModel::simulateCrash() {
+ ASSERT_NE(nullptr, mDeathRecipient.get());
+
+ // Currently, the utils::PreparedModel will not use the `cookie` or `who` arguments, so we pass
+ // in 0 and nullptr for these arguments instead. Normally, they are used by the
+ // hidl_death_recipient to determine which object is dead. However, the utils::PreparedModel
+ // code only pairs a single death recipient with a single HIDL interface object, so these
+ // arguments are redundant.
+ mDeathRecipient->serviceDied(0, nullptr);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
diff --git a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
new file mode 100644
index 0000000..5062ac9
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,341 @@
+/*
+ * 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.
+ */
+
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.2/PreparedModel.h>
+
+#include <functional>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const sp<V1_2::IPreparedModel> kInvalidPreparedModel;
+constexpr auto kNoTiming = V1_2::Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
+ .timeInDriver = std::numeric_limits<uint64_t>::max()};
+
+sp<MockPreparedModel> createMockPreparedModel() {
+ const auto mockPreparedModel = MockPreparedModel::create();
+
+ // Ensure that older calls are not used.
+ EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(0);
+
+ return mockPreparedModel;
+}
+
+auto makeExecuteSynchronously(V1_0::ErrorStatus status,
+ const std::vector<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) {
+ return [status, outputShapes, timing](const V1_0::Request& /*request*/,
+ V1_2::MeasureTiming /*measureTiming*/,
+ const V1_2::IPreparedModel::executeSynchronously_cb& cb) {
+ cb(status, outputShapes, timing);
+ return hardware::Void();
+ };
+}
+auto makeExecuteAsynchronously(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus,
+ const std::vector<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) {
+ return [launchStatus, returnStatus, outputShapes, timing](
+ const V1_0::Request& /*request*/, V1_2::MeasureTiming /*measureTiming*/,
+ const sp<V1_2::IExecutionCallback>& cb) -> Return<V1_0::ErrorStatus> {
+ cb->notify_1_2(returnStatus, outputShapes, timing);
+ return launchStatus;
+ };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+ return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+} // namespace
+
+TEST(PreparedModelTest, invalidPreparedModel) {
+ // run test
+ const auto result = PreparedModel::create(kInvalidPreparedModel, /*executeSynchronously=*/true);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathError) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto ret = []() -> Return<bool> { return false; };
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathTransportFailure) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathDeadObject) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeSync) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteSynchronously(V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+ // 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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ makeExecuteSynchronously(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+ // 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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).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, executeAsync) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(V1_0::ErrorStatus::NONE,
+ V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ EXPECT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeAsyncLaunchError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(V1_0::ErrorStatus::GENERAL_FAILURE,
+ V1_0::ErrorStatus::GENERAL_FAILURE, {},
+ kNoTiming)));
+
+ // 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, executeAsyncReturnError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(
+ V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+ // 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, executeAsyncTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .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, executeAsyncDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+ .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, executeAsyncCrash) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+ mockPreparedModel->simulateCrash();
+ return V1_0::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // 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, executeFencedNotSupported) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+
+TEST(PreparedModelTest, getUnderlyingResource) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+ // run test
+ const auto resource = preparedModel->getUnderlyingResource();
+
+ // verify resource
+ const sp<V1_2::IPreparedModel>* maybeMock = std::any_cast<sp<V1_2::IPreparedModel>>(&resource);
+ ASSERT_NE(maybeMock, nullptr);
+ EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.3/utils/Android.bp b/neuralnetworks/1.3/utils/Android.bp
index d5d897d..41d9521 100644
--- a/neuralnetworks/1.3/utils/Android.bp
+++ b/neuralnetworks/1.3/utils/Android.bp
@@ -38,3 +38,35 @@
"neuralnetworks_utils_hal_common",
],
}
+
+cc_test {
+ name: "neuralnetworks_utils_hal_1_3_test",
+ srcs: ["test/*.cpp"],
+ static_libs: [
+ "android.hardware.neuralnetworks@1.0",
+ "android.hardware.neuralnetworks@1.1",
+ "android.hardware.neuralnetworks@1.2",
+ "android.hardware.neuralnetworks@1.3",
+ "libgmock",
+ "libneuralnetworks_common",
+ "neuralnetworks_types",
+ "neuralnetworks_utils_hal_common",
+ "neuralnetworks_utils_hal_1_0",
+ "neuralnetworks_utils_hal_1_1",
+ "neuralnetworks_utils_hal_1_2",
+ "neuralnetworks_utils_hal_1_3",
+ ],
+ shared_libs: [
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libnativewindow",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
index 637179d..fda79c8 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
@@ -24,8 +24,12 @@
#include <nnapi/Types.h>
#include <memory>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes.
+
namespace android::hardware::neuralnetworks::V1_3::utils {
+// Class that adapts V1_3::IBuffer to nn::IBuffer.
class Buffer final : public nn::IBuffer {
struct PrivateConstructorTag {};
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
index d46b111..643172e 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
@@ -34,8 +34,31 @@
#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_3::utils {
+// Converts the results of IDevice::getSupportedOperations* to the NN canonical format. On success,
+// this function returns with the supported operations as indicated by a driver. On failure, this
+// function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
+ ErrorStatus status, const hidl_vec<bool>& supportedOperations);
+
+// 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_R. On failure, this function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+ ErrorStatus status, const sp<IPreparedModel>& preparedModel);
+
+// Converts the results of IDevice::execute* to the NN canonical format. On success, this function
+// returns with the output shapes and the timing information. On failure, this function returns with
+// the appropriate nn::ExecutionError.
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+ ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing);
+
+// A HIDL callback class to receive the results of IDevice::prepareModel* asynchronously.
class PreparedModelCallback final : public IPreparedModelCallback,
public hal::utils::IProtectedCallback {
public:
@@ -52,11 +75,10 @@
Data get();
private:
- void notifyInternal(Data result);
-
hal::utils::TransferValue<Data> mData;
};
+// A HIDL callback class to receive the results of IDevice::execute_1_3 asynchronously.
class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
public:
using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
@@ -73,8 +95,6 @@
Data get();
private:
- void notifyInternal(Data result);
-
hal::utils::TransferValue<Data> mData;
};
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 9653a05..74a6534 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
@@ -44,7 +44,7 @@
const hal::V1_3::Request::MemoryPool& memoryPool);
GeneralResult<OptionalTimePoint> unvalidatedConvert(
const hal::V1_3::OptionalTimePoint& optionalTimePoint);
-GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
+GeneralResult<OptionalDuration> unvalidatedConvert(
const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration);
GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_3::ErrorStatus& errorStatus);
@@ -54,7 +54,7 @@
GeneralResult<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc);
GeneralResult<Request> convert(const hal::V1_3::Request& request);
GeneralResult<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint);
-GeneralResult<OptionalTimeoutDuration> convert(
+GeneralResult<OptionalDuration> convert(
const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration);
GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus);
@@ -86,7 +86,7 @@
nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
const nn::OptionalTimePoint& optionalTimePoint);
nn::GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
- const nn::OptionalTimeoutDuration& optionalTimeoutDuration);
+ const nn::OptionalDuration& optionalTimeoutDuration);
nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus);
nn::GeneralResult<Priority> convert(const nn::Priority& priority);
@@ -96,13 +96,24 @@
nn::GeneralResult<Request> convert(const nn::Request& request);
nn::GeneralResult<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint);
nn::GeneralResult<OptionalTimeoutDuration> convert(
- const nn::OptionalTimeoutDuration& optionalTimeoutDuration);
+ const nn::OptionalDuration& optionalTimeoutDuration);
nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle);
nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory);
nn::GeneralResult<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles);
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<V1_1::ExecutionPreference> convert(
+ const nn::ExecutionPreference& executionPreference);
+nn::GeneralResult<hidl_vec<V1_2::Extension>> convert(const std::vector<nn::Extension>& extensions);
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles);
+nn::GeneralResult<hidl_vec<V1_2::OutputShape>> convert(
+ const std::vector<nn::OutputShape>& outputShapes);
+nn::GeneralResult<V1_2::DeviceType> convert(const nn::DeviceType& deviceType);
+nn::GeneralResult<V1_2::MeasureTiming> convert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<V1_2::Timing> convert(const nn::Timing& timing);
+
} // namespace android::hardware::neuralnetworks::V1_3::utils
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_CONVERSIONS_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
index 0f5234b..84f606a 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
@@ -32,8 +32,12 @@
#include <string>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_3::utils {
+// Class that adapts V1_3::IDevice to nn::IDevice.
class Device final : public nn::IDevice {
struct PrivateConstructorTag {};
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
index e0d69dd..664d87a 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
@@ -29,28 +29,32 @@
#include <utility>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_3::utils {
+// Class that adapts V1_3::IPreparedModel to nn::IPreparedModel.
class PreparedModel final : public nn::IPreparedModel {
struct PrivateConstructorTag {};
public:
static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
- sp<V1_3::IPreparedModel> preparedModel);
+ sp<V1_3::IPreparedModel> preparedModel, bool executeSynchronously);
- PreparedModel(PrivateConstructorTag tag, sp<V1_3::IPreparedModel> preparedModel,
- hal::utils::DeathHandler deathHandler);
+ PreparedModel(PrivateConstructorTag tag, bool executeSynchronously,
+ sp<V1_3::IPreparedModel> preparedModel, hal::utils::DeathHandler deathHandler);
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
const nn::Request& request, nn::MeasureTiming measure,
const nn::OptionalTimePoint& deadline,
- const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+ 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::OptionalTimeoutDuration& loopTimeoutDuration,
- const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+ const nn::OptionalDuration& loopTimeoutDuration,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
std::any getUnderlyingResource() const override;
@@ -62,6 +66,7 @@
const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
const OptionalTimeoutDuration& loopTimeoutDuration) const;
+ const bool kExecuteSynchronously;
const sp<V1_3::IPreparedModel> kPreparedModel;
const hal::utils::DeathHandler kDeathHandler;
};
diff --git a/neuralnetworks/1.3/utils/src/Buffer.cpp b/neuralnetworks/1.3/utils/src/Buffer.cpp
index ffdeccd..614033e 100644
--- a/neuralnetworks/1.3/utils/src/Buffer.cpp
+++ b/neuralnetworks/1.3/utils/src/Buffer.cpp
@@ -33,17 +33,18 @@
#include <memory>
#include <utility>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes.
+
namespace android::hardware::neuralnetworks::V1_3::utils {
nn::GeneralResult<std::shared_ptr<const Buffer>> Buffer::create(
sp<V1_3::IBuffer> buffer, nn::Request::MemoryDomainToken token) {
if (buffer == nullptr) {
- return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
- << "V1_3::utils::Buffer::create must have non-null buffer";
+ return NN_ERROR() << "V1_3::utils::Buffer::create must have non-null buffer";
}
if (token == static_cast<nn::Request::MemoryDomainToken>(0)) {
- return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
- << "V1_3::utils::Buffer::create must have non-zero token";
+ return NN_ERROR() << "V1_3::utils::Buffer::create must have non-zero token";
}
return std::make_shared<const Buffer>(PrivateConstructorTag{}, std::move(buffer), token);
@@ -65,10 +66,7 @@
const auto ret = kBuffer->copyTo(hidlDst);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "IBuffer::copyTo failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "IBuffer::copyTo failed with " << toString(status);
return {};
}
@@ -80,10 +78,7 @@
const auto ret = kBuffer->copyFrom(hidlSrc, hidlDimensions);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "IBuffer::copyFrom failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "IBuffer::copyFrom failed with " << toString(status);
return {};
}
diff --git a/neuralnetworks/1.3/utils/src/Callbacks.cpp b/neuralnetworks/1.3/utils/src/Callbacks.cpp
index e3c6074..af76e6a 100644
--- a/neuralnetworks/1.3/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.3/utils/src/Callbacks.cpp
@@ -30,6 +30,7 @@
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/1.2/Callbacks.h>
#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/1.2/PreparedModel.h>
#include <nnapi/hal/CommonUtils.h>
@@ -39,139 +40,99 @@
#include <utility>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_3::utils {
namespace {
-nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
- const sp<V1_0::IPreparedModel>& preparedModel) {
- return NN_TRY(V1_0::utils::PreparedModel::create(preparedModel));
-}
-
-nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
- const sp<V1_2::IPreparedModel>& preparedModel) {
- return NN_TRY(V1_2::utils::PreparedModel::create(preparedModel));
-}
-
-nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
- const sp<IPreparedModel>& preparedModel) {
- return NN_TRY(utils::PreparedModel::create(preparedModel));
-}
-
nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
convertExecutionGeneralResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
const V1_2::Timing& timing) {
return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
}
-nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
-convertExecutionGeneralResults(const hidl_vec<V1_2::OutputShape>& outputShapes,
- const V1_2::Timing& timing) {
+} // namespace
+
+nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
+ ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
+ HANDLE_HAL_STATUS(status) << "get supported operations failed with " << toString(status);
+ return supportedOperations;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+ ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
+ HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ return NN_TRY(PreparedModel::create(preparedModel, /*executeSynchronously=*/true));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+ ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) {
+ if (status == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+ auto canonicalOutputShapes =
+ nn::convert(outputShapes).value_or(std::vector<nn::OutputShape>{});
+ return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
+ << "execution failed with " << toString(status);
+ }
+ HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
return hal::utils::makeExecutionFailure(
convertExecutionGeneralResultsHelper(outputShapes, timing));
}
-} // namespace
-
Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
const sp<V1_0::IPreparedModel>& preparedModel) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
- } else if (preparedModel == nullptr) {
- notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Returned preparedModel is nullptr");
- } else {
- notifyInternal(convertPreparedModel(preparedModel));
- }
+ mData.put(V1_0::utils::prepareModelCallback(status, preparedModel));
return Void();
}
Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
const sp<V1_2::IPreparedModel>& preparedModel) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
- } else if (preparedModel == nullptr) {
- notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Returned preparedModel is nullptr");
- } else {
- notifyInternal(convertPreparedModel(preparedModel));
- }
+ mData.put(V1_2::utils::prepareModelCallback(status, preparedModel));
return Void();
}
Return<void> PreparedModelCallback::notify_1_3(ErrorStatus status,
const sp<IPreparedModel>& preparedModel) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
- } else if (preparedModel == nullptr) {
- notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Returned preparedModel is nullptr");
- } else {
- notifyInternal(convertPreparedModel(preparedModel));
- }
+ mData.put(prepareModelCallback(status, preparedModel));
return Void();
}
void PreparedModelCallback::notifyAsDeadObject() {
- notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+ mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
}
PreparedModelCallback::Data PreparedModelCallback::get() {
return mData.take();
}
-void PreparedModelCallback::notifyInternal(PreparedModelCallback::Data result) {
- mData.put(std::move(result));
-}
-
// ExecutionCallback methods begin here
Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
- } else {
- notifyInternal({});
- }
+ mData.put(V1_0::utils::executionCallback(status));
return Void();
}
Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus status,
const hidl_vec<V1_2::OutputShape>& outputShapes,
const V1_2::Timing& timing) {
- if (status != V1_0::ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
- } else {
- notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
- }
+ mData.put(V1_2::utils::executionCallback(status, outputShapes, timing));
return Void();
}
Return<void> ExecutionCallback::notify_1_3(ErrorStatus status,
const hidl_vec<V1_2::OutputShape>& outputShapes,
const V1_2::Timing& timing) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
- } else {
- notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
- }
+ mData.put(executionCallback(status, outputShapes, timing));
return Void();
}
void ExecutionCallback::notifyAsDeadObject() {
- notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+ mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
}
ExecutionCallback::Data ExecutionCallback::get() {
return mData.take();
}
-void ExecutionCallback::notifyInternal(ExecutionCallback::Data result) {
- mData.put(std::move(result));
-}
-
} // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 949dd0d..8b7db2b 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -272,47 +272,26 @@
GeneralResult<OptionalTimePoint> unvalidatedConvert(
const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
- constexpr auto kTimePointMaxCount = TimePoint::max().time_since_epoch().count();
- const auto makeTimePoint = [](uint64_t count) -> GeneralResult<OptionalTimePoint> {
- if (count > kTimePointMaxCount) {
- return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Unable to unvalidatedConvert OptionalTimePoint because the count exceeds "
- "the max";
- }
- const auto nanoseconds = std::chrono::nanoseconds{count};
- return TimePoint{nanoseconds};
- };
-
using Discriminator = hal::V1_3::OptionalTimePoint::hidl_discriminator;
switch (optionalTimePoint.getDiscriminator()) {
case Discriminator::none:
- return std::nullopt;
+ return {};
case Discriminator::nanosecondsSinceEpoch:
- return makeTimePoint(optionalTimePoint.nanosecondsSinceEpoch());
+ return TimePoint{Duration{optionalTimePoint.nanosecondsSinceEpoch()}};
}
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
<< "Invalid OptionalTimePoint discriminator "
<< underlyingType(optionalTimePoint.getDiscriminator());
}
-GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
+GeneralResult<OptionalDuration> unvalidatedConvert(
const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration) {
- constexpr auto kTimeoutDurationMaxCount = TimeoutDuration::max().count();
- const auto makeTimeoutDuration = [](uint64_t count) -> GeneralResult<OptionalTimeoutDuration> {
- if (count > kTimeoutDurationMaxCount) {
- return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Unable to unvalidatedConvert OptionalTimeoutDuration because the count "
- "exceeds the max";
- }
- return TimeoutDuration{count};
- };
-
using Discriminator = hal::V1_3::OptionalTimeoutDuration::hidl_discriminator;
switch (optionalTimeoutDuration.getDiscriminator()) {
case Discriminator::none:
- return std::nullopt;
+ return {};
case Discriminator::nanoseconds:
- return makeTimeoutDuration(optionalTimeoutDuration.nanoseconds());
+ return Duration(optionalTimeoutDuration.nanoseconds());
}
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
<< "Invalid OptionalTimeoutDuration discriminator "
@@ -360,7 +339,7 @@
return validatedConvert(optionalTimePoint);
}
-GeneralResult<OptionalTimeoutDuration> convert(
+GeneralResult<OptionalDuration> convert(
const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration) {
return validatedConvert(optionalTimeoutDuration);
}
@@ -629,27 +608,16 @@
OptionalTimePoint ret;
if (optionalTimePoint.has_value()) {
const auto count = optionalTimePoint.value().time_since_epoch().count();
- if (count < 0) {
- return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Unable to unvalidatedConvert OptionalTimePoint because time since epoch "
- "count is "
- "negative";
- }
ret.nanosecondsSinceEpoch(count);
}
return ret;
}
nn::GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
- const nn::OptionalTimeoutDuration& optionalTimeoutDuration) {
+ const nn::OptionalDuration& optionalTimeoutDuration) {
OptionalTimeoutDuration ret;
if (optionalTimeoutDuration.has_value()) {
const auto count = optionalTimeoutDuration.value().count();
- if (count < 0) {
- return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Unable to unvalidatedConvert OptionalTimeoutDuration because count is "
- "negative";
- }
ret.nanoseconds(count);
}
return ret;
@@ -697,7 +665,7 @@
}
nn::GeneralResult<OptionalTimeoutDuration> convert(
- const nn::OptionalTimeoutDuration& optionalTimeoutDuration) {
+ const nn::OptionalDuration& optionalTimeoutDuration) {
return validatedConvert(optionalTimeoutDuration);
}
@@ -717,4 +685,38 @@
return validatedConvert(bufferRoles);
}
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+ return V1_2::utils::convert(deviceStatus);
+}
+
+nn::GeneralResult<V1_1::ExecutionPreference> convert(
+ const nn::ExecutionPreference& executionPreference) {
+ return V1_2::utils::convert(executionPreference);
+}
+
+nn::GeneralResult<hidl_vec<V1_2::Extension>> convert(const std::vector<nn::Extension>& extensions) {
+ return V1_2::utils::convert(extensions);
+}
+
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles) {
+ return V1_2::utils::convert(handles);
+}
+
+nn::GeneralResult<hidl_vec<V1_2::OutputShape>> convert(
+ const std::vector<nn::OutputShape>& outputShapes) {
+ return V1_2::utils::convert(outputShapes);
+}
+
+nn::GeneralResult<V1_2::DeviceType> convert(const nn::DeviceType& deviceType) {
+ return V1_2::utils::convert(deviceType);
+}
+
+nn::GeneralResult<V1_2::MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
+ return V1_2::utils::convert(measureTiming);
+}
+
+nn::GeneralResult<V1_2::Timing> convert(const nn::Timing& timing) {
+ return V1_2::utils::convert(timing);
+}
+
} // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
index 82837ba..d710b85 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -36,6 +36,7 @@
#include <nnapi/hal/1.1/Conversions.h>
#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/1.2/Device.h>
+#include <nnapi/hal/1.2/Utils.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
#include <nnapi/hal/ProtectCallback.h>
@@ -47,6 +48,9 @@
#include <string>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_3::utils {
namespace {
@@ -66,29 +70,27 @@
return hidlPreparedModels;
}
-nn::GeneralResult<nn::SharedBuffer> convert(
- nn::GeneralResult<std::shared_ptr<const Buffer>> result) {
- return NN_TRY(std::move(result));
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
+ const Capabilities& capabilities) {
+ HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+ return nn::convert(capabilities);
}
-nn::GeneralResult<nn::Capabilities> initCapabilities(V1_3::IDevice* device) {
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_3::IDevice* device) {
CHECK(device != nullptr);
- nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- const auto cb = [&result](ErrorStatus status, const Capabilities& capabilities) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "getCapabilities_1_3 failed with " << toString(status);
- } else {
- result = nn::convert(capabilities);
- }
- };
+ auto cb = hal::utils::CallbackValue(capabilitiesCallback);
const auto ret = device->getCapabilities_1_3(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
+}
+
+nn::GeneralResult<nn::SharedBuffer> allocationCallback(ErrorStatus status,
+ const sp<IBuffer>& buffer, uint32_t token) {
+ HANDLE_HAL_STATUS(status) << "IDevice::allocate failed with " << toString(status);
+ return Buffer::create(buffer, static_cast<nn::Request::MemoryDomainToken>(token));
}
} // namespace
@@ -104,12 +106,12 @@
<< "V1_3::utils::Device::create must have non-null device";
}
- auto versionString = NN_TRY(V1_2::utils::initVersionString(device.get()));
- const auto deviceType = NN_TRY(V1_2::utils::initDeviceType(device.get()));
- auto extensions = NN_TRY(V1_2::utils::initExtensions(device.get()));
- auto capabilities = NN_TRY(initCapabilities(device.get()));
+ auto versionString = NN_TRY(V1_2::utils::getVersionStringFrom(device.get()));
+ const auto deviceType = NN_TRY(V1_2::utils::getDeviceTypeFrom(device.get()));
+ auto extensions = NN_TRY(V1_2::utils::getSupportedExtensionsFrom(device.get()));
+ auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
const auto numberOfCacheFilesNeeded =
- NN_TRY(V1_2::utils::initNumberOfCacheFilesNeeded(device.get()));
+ NN_TRY(V1_2::utils::getNumberOfCacheFilesNeededFrom(device.get()));
auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
return std::make_shared<const Device>(
@@ -174,27 +176,12 @@
const auto hidlModel = NN_TRY(convert(modelInShared));
- nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- auto cb = [&result, &model](ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical)
- << "IDevice::getSupportedOperations_1_3 failed with " << toString(status);
- } else if (supportedOperations.size() != model.main.operations.size()) {
- result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "IDevice::getSupportedOperations_1_3 returned vector of size "
- << supportedOperations.size() << " but expected "
- << model.main.operations.size();
- } else {
- result = supportedOperations;
- }
- };
+ auto cb = hal::utils::CallbackValue(supportedOperationsCallback);
const auto ret = kDevice->getSupportedOperations_1_3(hidlModel, cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
@@ -207,12 +194,12 @@
NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
const auto hidlModel = NN_TRY(convert(modelInShared));
- const auto hidlPreference = NN_TRY(V1_1::utils::convert(preference));
+ const auto hidlPreference = NN_TRY(convert(preference));
const auto hidlPriority = NN_TRY(convert(priority));
const auto hidlDeadline = NN_TRY(convert(deadline));
- const auto hidlModelCache = NN_TRY(V1_2::utils::convert(modelCache));
- const auto hidlDataCache = NN_TRY(V1_2::utils::convert(dataCache));
- const auto hidlToken = token;
+ const auto hidlModelCache = NN_TRY(convert(modelCache));
+ const auto hidlDataCache = NN_TRY(convert(dataCache));
+ const auto hidlToken = V1_2::utils::CacheToken{token};
const auto cb = sp<PreparedModelCallback>::make();
const auto scoped = kDeathHandler.protectCallback(cb.get());
@@ -221,10 +208,7 @@
kDevice->prepareModel_1_3(hidlModel, hidlPreference, hidlPriority, hidlDeadline,
hidlModelCache, hidlDataCache, hidlToken, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "prepareModel_1_3 failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
return cb->get();
}
@@ -233,9 +217,9 @@
nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
const auto hidlDeadline = NN_TRY(convert(deadline));
- const auto hidlModelCache = NN_TRY(V1_2::utils::convert(modelCache));
- const auto hidlDataCache = NN_TRY(V1_2::utils::convert(dataCache));
- const auto hidlToken = token;
+ const auto hidlModelCache = NN_TRY(convert(modelCache));
+ const auto hidlDataCache = NN_TRY(convert(dataCache));
+ const auto hidlToken = V1_2::utils::CacheToken{token};
const auto cb = sp<PreparedModelCallback>::make();
const auto scoped = kDeathHandler.protectCallback(cb.get());
@@ -243,10 +227,7 @@
const auto ret = kDevice->prepareModelFromCache_1_3(hidlDeadline, hidlModelCache, hidlDataCache,
hidlToken, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "prepareModelFromCache_1_3 failed with " << toString(status);
- }
+ HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
return cb->get();
}
@@ -260,27 +241,13 @@
const auto hidlInputRoles = NN_TRY(convert(inputRoles));
const auto hidlOutputRoles = NN_TRY(convert(outputRoles));
- nn::GeneralResult<nn::SharedBuffer> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "uninitialized";
- auto cb = [&result](ErrorStatus status, const sp<IBuffer>& buffer, uint32_t token) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "IDevice::allocate failed with " << toString(status);
- } else if (buffer == nullptr) {
- result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Returned buffer is nullptr";
- } else if (token == 0) {
- result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Returned token is invalid (0)";
- } else {
- result = convert(
- Buffer::create(buffer, static_cast<nn::Request::MemoryDomainToken>(token)));
- }
- };
+ auto cb = hal::utils::CallbackValue(allocationCallback);
const auto ret =
kDevice->allocate(hidlDesc, hidlPreparedModels, hidlInputRoles, hidlOutputRoles, cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
} // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index 49b9b0b..7b4b7ba 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -39,28 +39,23 @@
#include <utility>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::V1_3::utils {
namespace {
-nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
-convertExecutionResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
- const V1_2::Timing& timing) {
- return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
-}
-
-nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
- const hidl_vec<V1_2::OutputShape>& outputShapes, const V1_2::Timing& timing) {
- return hal::utils::makeExecutionFailure(convertExecutionResultsHelper(outputShapes, timing));
-}
-
nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionCallbackResults(
- const V1_2::Timing& timingLaunched, const V1_2::Timing& timingFenced) {
+ ErrorStatus status, const V1_2::Timing& timingLaunched, const V1_2::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)));
}
-nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
-convertExecuteFencedResults(const hidl_handle& syncFence,
- const sp<IFencedExecutionCallback>& callback) {
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> fencedExecutionCallback(
+ ErrorStatus status, const hidl_handle& syncFence,
+ const sp<IFencedExecutionCallback>& callback) {
+ HANDLE_HAL_STATUS(status) << "fenced execution failed with " << toString(status);
+
auto resultSyncFence = nn::SyncFence::createAsSignaled();
if (syncFence.getNativeHandle() != nullptr) {
auto sharedHandle = NN_TRY(nn::convert(syncFence));
@@ -75,23 +70,12 @@
// 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>> {
- nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> result =
- NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
- auto cb = [&result](ErrorStatus status, const V1_2::Timing& timingLaunched,
- const V1_2::Timing& timingFenced) {
- if (status != ErrorStatus::NONE) {
- const auto canonical =
- nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "getExecutionInfo failed with " << toString(status);
- } else {
- result = convertFencedExecutionCallbackResults(timingLaunched, timingFenced);
- }
- };
+ auto cb = hal::utils::CallbackValue(convertFencedExecutionCallbackResults);
const auto ret = callback->getExecutionInfo(cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
};
return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
@@ -100,42 +84,34 @@
} // namespace
nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
- sp<V1_3::IPreparedModel> preparedModel) {
+ sp<V1_3::IPreparedModel> preparedModel, bool executeSynchronously) {
if (preparedModel == nullptr) {
- return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
- << "V1_3::utils::PreparedModel::create must have non-null preparedModel";
+ return NN_ERROR() << "V1_3::utils::PreparedModel::create must have non-null preparedModel";
}
auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
- return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
- std::move(deathHandler));
+ return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, executeSynchronously,
+ std::move(preparedModel), std::move(deathHandler));
}
-PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_3::IPreparedModel> preparedModel,
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, bool executeSynchronously,
+ sp<V1_3::IPreparedModel> preparedModel,
hal::utils::DeathHandler deathHandler)
- : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
+ : kExecuteSynchronously(executeSynchronously),
+ kPreparedModel(std::move(preparedModel)),
+ kDeathHandler(std::move(deathHandler)) {}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
PreparedModel::executeSynchronously(const Request& request, V1_2::MeasureTiming measure,
const OptionalTimePoint& deadline,
const OptionalTimeoutDuration& loopTimeoutDuration) const {
- nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
- NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
- const auto cb = [&result](ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
- const V1_2::Timing& timing) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
- } else {
- result = convertExecutionResults(outputShapes, timing);
- }
- };
+ auto cb = hal::utils::CallbackValue(executionCallback);
const auto ret = kPreparedModel->executeSynchronously_1_3(request, measure, deadline,
loopTimeoutDuration, cb);
HANDLE_TRANSPORT_FAILURE(ret);
- return result;
+ return cb.take();
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
@@ -148,9 +124,8 @@
const auto ret =
kPreparedModel->execute_1_3(request, measure, deadline, loopTimeoutDuration, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- return NN_ERROR(canonical) << "executeAsynchronously failed with " << toString(status);
+ if (status != ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+ HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
}
return cb->get();
@@ -159,49 +134,36 @@
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::OptionalTimeoutDuration& loopTimeoutDuration) const {
+ 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 hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
- const auto hidlMeasure =
- NN_TRY(hal::utils::makeExecutionFailure(V1_2::utils::convert(measure)));
+ const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
const auto hidlLoopTimeoutDuration =
NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
- nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
- NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
- const bool preferSynchronous = true;
+ auto result = kExecuteSynchronously
+ ? executeSynchronously(hidlRequest, hidlMeasure, hidlDeadline,
+ hidlLoopTimeoutDuration)
+ : executeAsynchronously(hidlRequest, hidlMeasure, hidlDeadline,
+ hidlLoopTimeoutDuration);
+ auto [outputShapes, timing] = NN_TRY(std::move(result));
- // Execute synchronously if allowed.
- if (preferSynchronous) {
- result = executeSynchronously(hidlRequest, hidlMeasure, hidlDeadline,
- hidlLoopTimeoutDuration);
- }
+ NN_TRY(hal::utils::makeExecutionFailure(
+ hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
- // Run asymchronous execution if execution has not already completed.
- if (!result.has_value()) {
- result = executeAsynchronously(hidlRequest, hidlMeasure, hidlDeadline,
- hidlLoopTimeoutDuration);
- }
-
- // Flush output buffers if suxcessful execution.
- if (result.has_value()) {
- NN_TRY(hal::utils::makeExecutionFailure(
- hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
- }
-
- return result;
+ 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::OptionalTimeoutDuration& loopTimeoutDuration,
- const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const {
+ 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 =
@@ -209,28 +171,18 @@
const auto hidlRequest = NN_TRY(convert(requestInShared));
const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
- const auto hidlMeasure = NN_TRY(V1_2::utils::convert(measure));
+ const auto hidlMeasure = NN_TRY(convert(measure));
const auto hidlDeadline = NN_TRY(convert(deadline));
const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
- nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> result =
- NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
- auto cb = [&result](ErrorStatus status, const hidl_handle& syncFence,
- const sp<IFencedExecutionCallback>& callback) {
- if (status != ErrorStatus::NONE) {
- const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
- result = NN_ERROR(canonical) << "executeFenced failed with " << toString(status);
- } else {
- result = convertExecuteFencedResults(syncFence, callback);
- }
- };
+ auto cb = hal::utils::CallbackValue(fencedExecutionCallback);
const auto ret = kPreparedModel->executeFenced(hidlRequest, hidlWaitFor, hidlMeasure,
hidlDeadline, hidlLoopTimeoutDuration,
hidlTimeoutDurationAfterFence, cb);
HANDLE_TRANSPORT_FAILURE(ret);
- auto [syncFence, callback] = NN_TRY(std::move(result));
+ auto [syncFence, callback] = NN_TRY(cb.take());
// 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.
diff --git a/neuralnetworks/1.3/utils/test/BufferTest.cpp b/neuralnetworks/1.3/utils/test/BufferTest.cpp
new file mode 100644
index 0000000..d892b87
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/BufferTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+#include "MockBuffer.h"
+
+#include <android/hardware/neuralnetworks/1.3/IBuffer.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/1.3/Buffer.h>
+
+#include <functional>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+const auto kMemory = nn::createSharedMemory(4).value();
+const sp<V1_3::IBuffer> kInvalidBuffer;
+constexpr auto kInvalidToken = nn::Request::MemoryDomainToken{0};
+constexpr auto kToken = nn::Request::MemoryDomainToken{1};
+
+std::function<hardware::Return<V1_3::ErrorStatus>()> makeFunctionReturn(V1_3::ErrorStatus status) {
+ return [status]() -> hardware::Return<V1_3::ErrorStatus> { return status; };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+ return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeSuccessful = makeFunctionReturn(V1_3::ErrorStatus::NONE);
+const auto makeGeneralError = makeFunctionReturn(V1_3::ErrorStatus::GENERAL_FAILURE);
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(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(makeSuccessful));
+
+ // 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(makeGeneralError));
+
+ // 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(makeSuccessful));
+
+ // 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(makeGeneralError));
+
+ // 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 android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/test/DeviceTest.cpp b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..f260990
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
@@ -0,0 +1,951 @@
+/*
+ * 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.
+ */
+
+#include "MockBuffer.h"
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.3/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+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 sp<V1_3::IDevice> kInvalidDevice;
+constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {
+ .execTime = std::numeric_limits<float>::max(),
+ .powerUsage = std::numeric_limits<float>::max()};
+
+template <typename... Args>
+auto makeCallbackReturn(Args&&... args) {
+ return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) {
+ std::apply(cb, argPack);
+ return Void();
+ };
+}
+
+sp<MockDevice> createMockDevice() {
+ const auto mockDevice = MockDevice::create();
+
+ // Setup default actions for each relevant call.
+ const auto getVersionString_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, kName);
+ const auto getType_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, V1_2::DeviceType::OTHER);
+ const auto getSupportedExtensions_ret =
+ makeCallbackReturn(V1_0::ErrorStatus::NONE, hidl_vec<V1_2::Extension>{});
+ const auto getNumberOfCacheFilesNeeded_ret = makeCallbackReturn(
+ V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles, nn::kMaxNumberOfCacheFiles);
+ const auto getCapabilities_ret = makeCallbackReturn(
+ V1_3::ErrorStatus::NONE,
+ V1_3::Capabilities{
+ .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+ .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+ .ifPerformance = kNoPerformanceInfo,
+ .whilePerformance = kNoPerformanceInfo,
+ });
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, getVersionString(_)).WillByDefault(Invoke(getVersionString_ret));
+ ON_CALL(*mockDevice, getType(_)).WillByDefault(Invoke(getType_ret));
+ ON_CALL(*mockDevice, getSupportedExtensions(_))
+ .WillByDefault(Invoke(getSupportedExtensions_ret));
+ ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+ .WillByDefault(Invoke(getNumberOfCacheFilesNeeded_ret));
+ ON_CALL(*mockDevice, getCapabilities_1_3(_)).WillByDefault(Invoke(getCapabilities_ret));
+
+ // Ensure that older calls are not used.
+ EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0);
+ EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(0);
+ EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(0);
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(0);
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _)).Times(0);
+ EXPECT_CALL(*mockDevice, prepareModel(_, _)).Times(0);
+ EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _)).Times(0);
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_2(_, _)).Times(0);
+ EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _)).Times(0);
+
+ // 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_1_3(_)).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+auto makePreparedModelReturn(V1_3::ErrorStatus launchStatus, V1_3::ErrorStatus returnStatus,
+ const sp<MockPreparedModel>& preparedModel) {
+ return [launchStatus, returnStatus, preparedModel](
+ const V1_3::Model& /*model*/, V1_1::ExecutionPreference /*preference*/,
+ V1_3::Priority /*priority*/, const V1_3::OptionalTimePoint& /*deadline*/,
+ const hardware::hidl_vec<hardware::hidl_handle>& /*modelCache*/,
+ const hardware::hidl_vec<hardware::hidl_handle>& /*dataCache*/,
+ const CacheToken& /*token*/, const sp<V1_3::IPreparedModelCallback>& cb)
+ -> hardware::Return<V1_3::ErrorStatus> {
+ cb->notify_1_3(returnStatus, preparedModel).isOk();
+ return launchStatus;
+ };
+}
+auto makePreparedModelFromCacheReturn(V1_3::ErrorStatus launchStatus,
+ V1_3::ErrorStatus returnStatus,
+ const sp<MockPreparedModel>& preparedModel) {
+ return [launchStatus, returnStatus, preparedModel](
+ const V1_3::OptionalTimePoint& /*deadline*/,
+ const hardware::hidl_vec<hardware::hidl_handle>& /*modelCache*/,
+ const hardware::hidl_vec<hardware::hidl_handle>& /*dataCache*/,
+ const CacheToken& /*token*/, const sp<V1_3::IPreparedModelCallback>& cb)
+ -> hardware::Return<V1_3::ErrorStatus> {
+ cb->notify_1_3(returnStatus, preparedModel).isOk();
+ return launchStatus;
+ };
+}
+auto makeAllocateReturn(ErrorStatus status, const sp<MockBuffer>& buffer, uint32_t token) {
+ return [status, buffer, token](
+ const V1_3::BufferDesc& /*desc*/,
+ const hardware::hidl_vec<sp<V1_3::IPreparedModel>>& /*preparedModels*/,
+ const hardware::hidl_vec<V1_3::BufferRole>& /*inputRoles*/,
+ const hardware::hidl_vec<V1_3::BufferRole>& /*outputRoles*/,
+ const V1_3::IDevice::allocate_cb& cb) -> hardware::Return<void> {
+ cb(status, buffer, token);
+ return hardware::Void();
+ };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+ return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(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();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, "");
+ EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret =
+ makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_2::DeviceType::OTHER);
+ EXPECT_CALL(*mockDevice, getType(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret =
+ makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, hidl_vec<V1_2::Extension>{});
+ EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+ nn::kMaxNumberOfCacheFiles, nn::kMaxNumberOfCacheFiles);
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles + 1,
+ nn::kMaxNumberOfCacheFiles);
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles,
+ nn::kMaxNumberOfCacheFiles + 1);
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = makeCallbackReturn(
+ V1_3::ErrorStatus::GENERAL_FAILURE,
+ V1_3::Capabilities{
+ .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+ .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+ .ifPerformance = kNoPerformanceInfo,
+ .whilePerformance = kNoPerformanceInfo,
+ });
+ EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(1).WillOnce(Invoke(ret));
+
+ // 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_1_3(_))
+ .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_1_3(_))
+ .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, linkToDeathError) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = []() -> Return<bool> { return false; };
+ EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // 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, linkToDeathTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, linkToDeathRet())
+ .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, linkToDeathDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, linkToDeathRet())
+ .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_R);
+}
+
+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_1_3(_)).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, wait) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto ret = []() -> Return<void> { return {}; };
+ EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(DeviceTest, waitTransportFailure) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, ping())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, waitDeadObject) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+ const auto device = Device::create(kName, mockDevice).value();
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+ // setup call
+ const auto mockDevice = createMockDevice();
+ const auto device = Device::create(kName, mockDevice).value();
+ const auto ret = [](const auto& model, const auto& cb) {
+ cb(V1_3::ErrorStatus::NONE, std::vector<bool>(model.main.operations.size(), true));
+ return hardware::Void();
+ };
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_3(_, _)).Times(1).WillOnce(Invoke(ret));
+
+ // 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();
+ const auto ret = [](const auto& /*model*/, const auto& cb) {
+ cb(V1_3::ErrorStatus::GENERAL_FAILURE, {});
+ return hardware::Void();
+ };
+ EXPECT_CALL(*mockDevice, getSupportedOperations_1_3(_, _)).Times(1).WillOnce(Invoke(ret));
+
+ // 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_1_3(_, _))
+ .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_1_3(_, _))
+ .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_1_3(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_3::ErrorStatus::NONE,
+ V1_3::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_1_3(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_3::ErrorStatus::GENERAL_FAILURE,
+ V1_3::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_1_3(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_3::ErrorStatus::NONE,
+ V1_3::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_1_3(_, _, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelReturn(V1_3::ErrorStatus::NONE,
+ V1_3::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_1_3(_, _, _, _, _, _, _, _))
+ .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_1_3(_, _, _, _, _, _, _, _))
+ .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 = [&mockDevice]() -> hardware::Return<V1_3::ErrorStatus> {
+ mockDevice->simulateCrash();
+ return V1_3::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _))
+ .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_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+ V1_3::ErrorStatus::NONE, V1_3::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, prepareModelFromCacheError) {
+ // 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::GENERAL_FAILURE,
+ 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();
+ 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::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_1_3(_, _, _, _, _))
+ .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_1_3(_, _, _, _, _))
+ .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 = [&mockDevice]() -> hardware::Return<V1_3::ErrorStatus> {
+ mockDevice->simulateCrash();
+ return V1_3::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+ .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 = MockBuffer::create();
+ constexpr uint32_t token = 1;
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeAllocateReturn(ErrorStatus::NONE, mockBuffer, token)));
+
+ // 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(Invoke(makeAllocateReturn(ErrorStatus::GENERAL_FAILURE, nullptr, 0)));
+
+ // 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 android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/test/MockBuffer.h b/neuralnetworks/1.3/utils/test/MockBuffer.h
new file mode 100644
index 0000000..fb31b51
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
+
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockBuffer final : public IBuffer {
+ public:
+ static sp<MockBuffer> create();
+
+ // V1_3 methods below.
+ MOCK_METHOD(Return<V1_3::ErrorStatus>, copyTo, (const hidl_memory& dst), (override));
+ MOCK_METHOD(Return<V1_3::ErrorStatus>, copyFrom,
+ (const hidl_memory& src, const hidl_vec<uint32_t>& dimensions), (override));
+};
+
+inline sp<MockBuffer> MockBuffer::create() {
+ return sp<MockBuffer>::make();
+}
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
diff --git a/neuralnetworks/1.3/utils/test/MockDevice.h b/neuralnetworks/1.3/utils/test/MockDevice.h
new file mode 100644
index 0000000..85d3750
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockDevice.h
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
+
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+using CacheToken =
+ hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+
+class MockDevice final : public IDevice {
+ public:
+ static sp<MockDevice> create();
+
+ // IBase methods below.
+ MOCK_METHOD(Return<void>, ping, (), (override));
+ MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+ Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t /*cookie*/);
+
+ // V1_0 methods below.
+ MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations,
+ (const V1_0::Model& model, getSupportedOperations_cb cb), (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel,
+ (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<V1_0::DeviceStatus>, getStatus, (), (override));
+
+ // V1_1 methods below.
+ MOCK_METHOD(Return<void>, getCapabilities_1_1, (getCapabilities_1_1_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations_1_1,
+ (const V1_1::Model& model, getSupportedOperations_1_1_cb cb), (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_1,
+ (const V1_1::Model& model, V1_1::ExecutionPreference preference,
+ const sp<V1_0::IPreparedModelCallback>& callback),
+ (override));
+
+ // V1_2 methods below.
+ MOCK_METHOD(Return<void>, getVersionString, (getVersionString_cb cb), (override));
+ MOCK_METHOD(Return<void>, getType, (getType_cb cb), (override));
+ MOCK_METHOD(Return<void>, getCapabilities_1_2, (getCapabilities_1_2_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedExtensions, (getSupportedExtensions_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations_1_2,
+ (const V1_2::Model& model, getSupportedOperations_1_2_cb cb), (override));
+ MOCK_METHOD(Return<void>, getNumberOfCacheFilesNeeded, (getNumberOfCacheFilesNeeded_cb cb),
+ (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_2,
+ (const V1_2::Model& model, V1_1::ExecutionPreference preference,
+ const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+ const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModelFromCache,
+ (const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+ const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback),
+ (override));
+
+ // V1_3 methods below.
+ MOCK_METHOD(Return<void>, getCapabilities_1_3, (getCapabilities_1_3_cb cb), (override));
+ MOCK_METHOD(Return<void>, getSupportedOperations_1_3,
+ (const V1_3::Model& model, getSupportedOperations_1_3_cb cb), (override));
+ MOCK_METHOD(Return<V1_3::ErrorStatus>, prepareModel_1_3,
+ (const V1_3::Model& model, V1_1::ExecutionPreference preference,
+ V1_3::Priority priority, const V1_3::OptionalTimePoint& deadline,
+ const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+ const CacheToken& token, const sp<V1_3::IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<V1_3::ErrorStatus>, prepareModelFromCache_1_3,
+ (const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
+ const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
+ const sp<V1_3::IPreparedModelCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<void>, allocate,
+ (const V1_3::BufferDesc& desc,
+ const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels,
+ const hidl_vec<V1_3::BufferRole>& inputRoles,
+ const hidl_vec<V1_3::BufferRole>& outputRoles, allocate_cb cb),
+ (override));
+
+ // Helper methods.
+ void simulateCrash();
+
+ private:
+ sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockDevice> MockDevice::create() {
+ auto mockDevice = sp<MockDevice>::make();
+
+ // Setup default actions for each relevant call.
+ const auto ret = []() -> Return<bool> { return true; };
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+inline Return<bool> MockDevice::linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) {
+ mDeathRecipient = recipient;
+ return linkToDeathRet();
+}
+
+inline void MockDevice::simulateCrash() {
+ ASSERT_NE(nullptr, mDeathRecipient.get());
+
+ // Currently, the utils::Device will not use the `cookie` or `who` arguments, so we pass in 0
+ // and nullptr for these arguments instead. Normally, they are used by the hidl_death_recipient
+ // to determine which object is dead. However, the utils::Device code only pairs a single death
+ // recipient with a single HIDL interface object, so these arguments are redundant.
+ mDeathRecipient->serviceDied(0, nullptr);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
diff --git a/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
new file mode 100644
index 0000000..fc08a7f
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+
+#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockFencedExecutionCallback final : public IFencedExecutionCallback {
+ public:
+ static sp<MockFencedExecutionCallback> create();
+
+ // V1_3 methods below.
+ MOCK_METHOD(Return<void>, getExecutionInfo, (IFencedExecutionCallback::getExecutionInfo_cb cb),
+ (override));
+};
+
+inline sp<MockFencedExecutionCallback> MockFencedExecutionCallback::create() {
+ return sp<MockFencedExecutionCallback>::make();
+}
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
diff --git a/neuralnetworks/1.3/utils/test/MockPreparedModel.h b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..e441524
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
+
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockPreparedModel final : public IPreparedModel {
+ public:
+ static sp<MockPreparedModel> create();
+
+ // IBase methods below.
+ MOCK_METHOD(Return<void>, ping, (), (override));
+ MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+ Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) override;
+
+ // V1_0 methods below.
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, execute,
+ (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback),
+ (override));
+
+ // V1_2 methods below.
+ MOCK_METHOD(Return<V1_0::ErrorStatus>, execute_1_2,
+ (const V1_0::Request& request, V1_2::MeasureTiming measure,
+ const sp<V1_2::IExecutionCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<void>, executeSynchronously,
+ (const V1_0::Request& request, V1_2::MeasureTiming measure,
+ executeSynchronously_cb cb),
+ (override));
+ MOCK_METHOD(Return<void>, configureExecutionBurst,
+ (const sp<V1_2::IBurstCallback>& callback,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
+ configureExecutionBurst_cb cb),
+ (override));
+
+ // V1_3 methods below.
+ MOCK_METHOD(Return<V1_3::ErrorStatus>, execute_1_3,
+ (const V1_3::Request& request, V1_2::MeasureTiming measure,
+ const V1_3::OptionalTimePoint& deadline,
+ const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+ const sp<V1_3::IExecutionCallback>& callback),
+ (override));
+ MOCK_METHOD(Return<void>, executeSynchronously_1_3,
+ (const V1_3::Request& request, V1_2::MeasureTiming measure,
+ const V1_3::OptionalTimePoint& deadline,
+ const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+ executeSynchronously_1_3_cb cb),
+ (override));
+ MOCK_METHOD(Return<void>, executeFenced,
+ (const V1_3::Request& request, const hidl_vec<hidl_handle>& waitFor,
+ V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline,
+ const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+ const V1_3::OptionalTimeoutDuration& duration, executeFenced_cb cb),
+ (override));
+
+ // Helper methods.
+ void simulateCrash();
+
+ private:
+ sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockPreparedModel> MockPreparedModel::create() {
+ auto mockPreparedModel = sp<MockPreparedModel>::make();
+
+ // Setup default actions for each relevant call.
+ const auto ret = []() -> Return<bool> { return true; };
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockPreparedModel, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(testing::AnyNumber());
+
+ return mockPreparedModel;
+}
+
+inline Return<bool> MockPreparedModel::linkToDeath(const sp<hidl_death_recipient>& recipient,
+ uint64_t /*cookie*/) {
+ mDeathRecipient = recipient;
+ return linkToDeathRet();
+}
+
+inline void MockPreparedModel::simulateCrash() {
+ ASSERT_NE(nullptr, mDeathRecipient.get());
+
+ // Currently, the utils::PreparedModel will not use the `cookie` or `who` arguments, so we pass
+ // in 0 and nullptr for these arguments instead. Normally, they are used by the
+ // hidl_death_recipient to determine which object is dead. However, the utils::PreparedModel
+ // code only pairs a single death recipient with a single HIDL interface object, so these
+ // arguments are redundant.
+ mDeathRecipient->serviceDied(0, nullptr);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
diff --git a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
new file mode 100644
index 0000000..11796dd
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,470 @@
+/*
+ * 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.
+ */
+
+#include "MockFencedExecutionCallback.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/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/1.3/PreparedModel.h>
+
+#include <functional>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const sp<V1_3::IPreparedModel> kInvalidPreparedModel;
+constexpr auto kNoTiming = V1_2::Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
+ .timeInDriver = std::numeric_limits<uint64_t>::max()};
+
+sp<MockPreparedModel> createMockPreparedModel() {
+ const auto mockPreparedModel = MockPreparedModel::create();
+
+ // Ensure that older calls are not used.
+ EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(0);
+ EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(0);
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _)).Times(0);
+
+ return mockPreparedModel;
+}
+
+auto makeExecuteSynchronously(V1_3::ErrorStatus status,
+ const std::vector<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) {
+ return [status, outputShapes, timing](
+ const V1_3::Request& /*request*/, V1_2::MeasureTiming /*measureTiming*/,
+ const V1_3::OptionalTimePoint& /*deadline*/,
+ const V1_3::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+ const V1_3::IPreparedModel::executeSynchronously_1_3_cb& cb) {
+ cb(status, outputShapes, timing);
+ return hardware::Void();
+ };
+}
+auto makeExecuteAsynchronously(V1_3::ErrorStatus launchStatus, V1_3::ErrorStatus returnStatus,
+ const std::vector<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) {
+ return [launchStatus, returnStatus, outputShapes, timing](
+ const V1_3::Request& /*request*/, V1_2::MeasureTiming /*measureTiming*/,
+ const V1_3::OptionalTimePoint& /*deadline*/,
+ const V1_3::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+ const sp<V1_3::IExecutionCallback>& cb) -> Return<V1_3::ErrorStatus> {
+ cb->notify_1_3(returnStatus, outputShapes, timing);
+ return launchStatus;
+ };
+}
+auto makeExecuteFencedReturn(V1_3::ErrorStatus status, const hardware::hidl_handle& syncFence,
+ const sp<V1_3::IFencedExecutionCallback>& dispatchCallback) {
+ return [status, syncFence, dispatchCallback](
+ const V1_3::Request& /*request*/,
+ const hardware::hidl_vec<hardware::hidl_handle>& /*waitFor*/,
+ V1_2::MeasureTiming /*measure*/, const V1_3::OptionalTimePoint& /*deadline*/,
+ const V1_3::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+ const V1_3::OptionalTimeoutDuration& /*duration*/,
+ const V1_3::IPreparedModel::executeFenced_cb& cb) {
+ cb(status, syncFence, dispatchCallback);
+ return hardware::Void();
+ };
+}
+auto makeExecuteFencedCallbackReturn(V1_3::ErrorStatus status, const V1_2::Timing& timingA,
+ const V1_2::Timing& timingB) {
+ return [status, timingA,
+ timingB](const V1_3::IFencedExecutionCallback::getExecutionInfo_cb& cb) {
+ cb(status, timingA, timingB);
+ return hardware::Void();
+ };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+ return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+} // namespace
+
+TEST(PreparedModelTest, invalidPreparedModel) {
+ // run test
+ const auto result = PreparedModel::create(kInvalidPreparedModel, /*executeSynchronously=*/true);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathError) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto ret = []() -> Return<bool> { return false; };
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathTransportFailure) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathDeadObject) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeSync) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteSynchronously(V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+ // 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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ makeExecuteSynchronously(V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+ // 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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+ .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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+ .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, executeAsync) {
+ // setup call
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(V1_3::ErrorStatus::NONE,
+ V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ EXPECT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeAsyncLaunchError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(V1_3::ErrorStatus::GENERAL_FAILURE,
+ V1_3::ErrorStatus::GENERAL_FAILURE, {},
+ kNoTiming)));
+
+ // 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, executeAsyncReturnError) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteAsynchronously(
+ V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+ // 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, executeAsyncTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .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, executeAsyncDeadObject) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .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, executeAsyncCrash) {
+ // setup test
+ const auto mockPreparedModel = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+ const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_3::ErrorStatus> {
+ mockPreparedModel->simulateCrash();
+ return V1_3::ErrorStatus::NONE;
+ };
+ EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(ret));
+
+ // 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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ const auto mockCallback = MockFencedExecutionCallback::create();
+ EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::NONE, kNoTiming,
+ kNoTiming)));
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, 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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ const auto mockCallback = MockFencedExecutionCallback::create();
+ EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::GENERAL_FAILURE,
+ kNoTiming, kNoTiming)));
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, 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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ makeExecuteFencedReturn(V1_3::ErrorStatus::GENERAL_FAILURE, {}, nullptr)));
+
+ // 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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).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 = createMockPreparedModel();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+ // run test
+ const auto resource = preparedModel->getUnderlyingResource();
+
+ // verify resource
+ const sp<V1_3::IPreparedModel>* maybeMock = std::any_cast<sp<V1_3::IPreparedModel>>(&resource);
+ ASSERT_NE(maybeMock, nullptr);
+ EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
+}
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
index ca5041d..de84624 100644
--- a/neuralnetworks/TEST_MAPPING
+++ b/neuralnetworks/TEST_MAPPING
@@ -1,6 +1,21 @@
{
"presubmit": [
{
+ "name": "neuralnetworks_utils_hal_common_test"
+ },
+ {
+ "name": "neuralnetworks_utils_hal_1_0_test"
+ },
+ {
+ "name": "neuralnetworks_utils_hal_1_1_test"
+ },
+ {
+ "name": "neuralnetworks_utils_hal_1_2_test"
+ },
+ {
+ "name": "neuralnetworks_utils_hal_1_3_test"
+ },
+ {
"name": "VtsHalNeuralnetworksV1_0TargetTest",
"options": [
{
diff --git a/neuralnetworks/utils/README.md b/neuralnetworks/utils/README.md
index 0dee103..45ca0b4 100644
--- a/neuralnetworks/utils/README.md
+++ b/neuralnetworks/utils/README.md
@@ -1,11 +1,11 @@
# NNAPI Conversions
`convert` fails if either the source type or the destination type is invalid, and it yields a valid
-object if the conversion succeeds. For example, let's say that an enumeration in the current
-version has fewer possible values than the "same" canonical enumeration, such as `OperationType`.
-The new value of `HARD_SWISH` (introduced in Android R / NN HAL 1.3) does not map to any valid
-existing value in `OperationType`, but an older value of `ADD` (introduced in Android OC-MR1 / NN
-HAL 1.0) is valid. This can be seen in the following model conversions:
+object if the conversion succeeds. For example, let's say that an enumeration in the current version
+has fewer possible values than the "same" canonical enumeration, such as `OperationType`. The new
+value of `HARD_SWISH` (introduced in Android R / NN HAL 1.3) does not map to any valid existing
+value in `OperationType`, but an older value of `ADD` (introduced in Android OC-MR1 / NN HAL 1.0) is
+valid. This can be seen in the following model conversions:
```cpp
// Unsuccessful conversion
@@ -48,3 +48,50 @@
`unvalidatedConvert` functions operate on types that are either used in a HIDL method call directly
(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
+
+Some notes about HIDL interface objects and lifetimes across processes:
+
+All HIDL interface objects inherit from `IBase`, which itself inherits from `::android::RefBase`. As
+such, all HIDL interface objects are reference counted and must be owned through `::android::sp` (or
+referenced through `::android::wp`). Allocating `RefBase` objects on the stack will log errors and
+may result in crashes, and deleting a `RefBase` object through another means (e.g., "delete",
+"free", or RAII-cleanup through `std::unique_ptr` or some equivalent) will result in double-free
+and/or use-after-free undefined behavior.
+
+HIDL/Binder manages the reference count of HIDL interface objects automatically across processes. If
+a process that references (but did not create) the HIDL interface object dies, HIDL/Binder ensures
+any reference count it held is properly released. (Caveat: it might be possible that HIDL/Binder
+behave strangely with `::android::wp` references.)
+
+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
+
+Some notes about asynchronous calls across HIDL:
+
+For synchronous calls across HIDL, if an error occurs after the function was called but before it
+returns, HIDL will return a transport error. For example, if the message cannot be delivered to the
+server process or if the server process dies before returning a result, HIDL will return from the
+function with the appropriate transport error in the `Return<>` object, which can be queried with
+`Return<>::isOk()`, `Return<>::isDeadObject()`, `Return<>::description()`, etc.
+
+However, HIDL offers no such error management in the case of asynchronous calls. By default, if the
+client launches an asynchronous task and the server fails to return a result through the callback,
+the client will be left waiting indefinitely for a result it will never receive.
+
+In the NNAPI, `IDevice::prepareModel*` and `IPreparedModel::execute*` (but not
+`IPreparedModel::executeSynchronously*`) are asynchronous calls across HIDL. Specifically, these
+asynchronous functions are called with a HIDL interface callback object (`IPrepareModelCallback` for
+`IDevice::prepareModel*` and `IExecutionCallback` for `IPreparedModel::execute*`) and are expected
+to quickly return, and the results are returned at a later time through these callback objects.
+
+To protect against the case when the server dies after the asynchronous task was called successfully
+but before the results could be returned, HIDL provides an object called a "`hidl_death_recipient`,"
+which can be used to detect when an interface object (and more generally, the server process) has
+died. nnapi/hal/ProtectCallback.h's `DeathHandler` uses `hidl_death_recipient`s to detect when the
+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()`.
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index 21562cf..6c491ae 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -28,3 +28,28 @@
"libhidlbase",
],
}
+
+cc_test {
+ name: "neuralnetworks_utils_hal_common_test",
+ srcs: ["test/*.cpp"],
+ static_libs: [
+ "android.hardware.neuralnetworks@1.0",
+ "libgmock",
+ "libneuralnetworks_common",
+ "neuralnetworks_types",
+ "neuralnetworks_utils_hal_common",
+ ],
+ shared_libs: [
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libnativewindow",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 43bb0c6..b3989e5 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -44,9 +44,18 @@
bool hasNoPointerData(const nn::Model& model);
bool hasNoPointerData(const nn::Request& request);
-// Relocate pointer-based data to shared memory.
+// Relocate pointer-based data to shared memory. If `model` has no Operand::LifeTime::POINTER data,
+// the function returns with a reference to `model`. If `model` has Operand::LifeTime::POINTER data,
+// the model is copied to `maybeModelInSharedOut` with the POINTER data relocated to a memory pool,
+// and the function returns with a reference to `*maybeModelInSharedOut`.
nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut);
+
+// Relocate pointer-based data to shared memory. If `request` has no
+// Request::Argument::LifeTime::POINTER data, the function returns with a reference to `request`. If
+// `request` has Request::Argument::LifeTime::POINTER data, the request is copied to
+// `maybeRequestInSharedOut` with the POINTER data relocated to a memory pool, and the function
+// returns with a reference to `*maybeRequestInSharedOut`.
nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut);
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
index 78b2a12..95a20a8 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
@@ -79,4 +79,11 @@
return makeExecutionFailure(makeGeneralFailure(result, status));
}
+#define HANDLE_HAL_STATUS(status) \
+ if (const auto canonical = ::android::nn::convert(status).value_or( \
+ ::android::nn::ErrorStatus::GENERAL_FAILURE); \
+ canonical == ::android::nn::ErrorStatus::NONE) { \
+ } else \
+ return NN_ERROR(canonical)
+
} // namespace android::hardware::neuralnetworks::utils
\ No newline at end of file
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
index 4b32b4e..985cddb 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
@@ -32,13 +32,13 @@
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
const nn::Request& request, nn::MeasureTiming measure,
const nn::OptionalTimePoint& deadline,
- const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+ 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::OptionalTimeoutDuration& loopTimeoutDuration,
- const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+ const nn::OptionalDuration& loopTimeoutDuration,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
std::any getUnderlyingResource() const override;
};
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
index 85bd613..c921885 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
@@ -28,6 +28,9 @@
#include <mutex>
#include <vector>
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
namespace android::hardware::neuralnetworks::utils {
class IProtectedCallback {
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
index 996ec1e..d2c2469 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
@@ -34,7 +34,7 @@
struct PrivateConstructorTag {};
public:
- using Factory = std::function<nn::GeneralResult<nn::SharedBuffer>(bool blocking)>;
+ using Factory = std::function<nn::GeneralResult<nn::SharedBuffer>()>;
static nn::GeneralResult<std::shared_ptr<const ResilientBuffer>> create(Factory makeBuffer);
@@ -42,7 +42,7 @@
nn::SharedBuffer buffer);
nn::SharedBuffer getBuffer() const;
- nn::SharedBuffer recover(const nn::IBuffer* failingBuffer, bool blocking) const;
+ nn::GeneralResult<nn::SharedBuffer> recover(const nn::IBuffer* failingBuffer) const;
nn::Request::MemoryDomainToken getToken() const override;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
index 4bfed6c..84ae799 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
@@ -46,8 +46,8 @@
nn::Capabilities capabilities, nn::SharedDevice device);
nn::SharedDevice getDevice() const EXCLUDES(mMutex);
- nn::SharedDevice recover(const nn::IDevice* failingDevice, bool blocking) const
- EXCLUDES(mMutex);
+ nn::GeneralResult<nn::SharedDevice> recover(const nn::IDevice* failingDevice,
+ bool blocking) const EXCLUDES(mMutex);
const std::string& getName() const override;
const std::string& getVersionString() const override;
@@ -81,17 +81,14 @@
private:
bool isValidInternal() const EXCLUDES(mMutex);
nn::GeneralResult<nn::SharedPreparedModel> prepareModelInternal(
- bool blocking, const nn::Model& model, nn::ExecutionPreference preference,
- nn::Priority priority, nn::OptionalTimePoint deadline,
- const std::vector<nn::SharedHandle>& modelCache,
+ 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;
nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCacheInternal(
- bool blocking, nn::OptionalTimePoint deadline,
- const std::vector<nn::SharedHandle>& modelCache,
+ nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const;
nn::GeneralResult<nn::SharedBuffer> allocateInternal(
- bool blocking, const nn::BufferDesc& desc,
- const std::vector<nn::SharedPreparedModel>& preparedModels,
+ const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
const std::vector<nn::BufferRole>& inputRoles,
const std::vector<nn::BufferRole>& outputRoles) const;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
index c2940d1..9b8d924 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
@@ -34,7 +34,7 @@
struct PrivateConstructorTag {};
public:
- using Factory = std::function<nn::GeneralResult<nn::SharedPreparedModel>(bool blocking)>;
+ using Factory = std::function<nn::GeneralResult<nn::SharedPreparedModel>()>;
static nn::GeneralResult<std::shared_ptr<const ResilientPreparedModel>> create(
Factory makePreparedModel);
@@ -43,19 +43,19 @@
nn::SharedPreparedModel preparedModel);
nn::SharedPreparedModel getPreparedModel() const;
- nn::SharedPreparedModel recover(const nn::IPreparedModel* failingPreparedModel,
- bool blocking) const;
+ nn::GeneralResult<nn::SharedPreparedModel> recover(
+ const nn::IPreparedModel* failingPreparedModel) const;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
const nn::Request& request, nn::MeasureTiming measure,
const nn::OptionalTimePoint& deadline,
- const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+ 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::OptionalTimeoutDuration& loopTimeoutDuration,
- const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+ const nn::OptionalDuration& loopTimeoutDuration,
+ const nn::OptionalDuration& timeoutDurationAfterFence) const override;
std::any getUnderlyingResource() const override;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h b/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h
index 7103c6b..6679afe 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h
@@ -17,19 +17,60 @@
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H
+#include <android-base/logging.h>
#include <android-base/thread_annotations.h>
#include <condition_variable>
+#include <functional>
#include <mutex>
#include <optional>
+#include <type_traits>
namespace android::hardware::neuralnetworks::utils {
-// This class is thread safe.
+// This class adapts a function pointer and offers two affordances:
+// 1) This class object can be used to generate a callback (via the implicit conversion operator)
+// that can be used to send the result to `CallbackValue` when called.
+// 2) This class object can be used to retrieve the result of the callback with `take`.
+//
+// This class is thread compatible.
+template <typename ReturnType, typename... ArgTypes>
+class CallbackValue final {
+ public:
+ using FunctionType = std::add_pointer_t<ReturnType(ArgTypes...)>;
+ using CallbackType = std::function<void(ArgTypes...)>;
+
+ explicit CallbackValue(FunctionType fn);
+
+ // Creates a callback that forwards its arguments to `mFunction` and stores the result in
+ // `mReturnValue`.
+ /*implicit*/ operator CallbackType(); // NOLINT(google-explicit-constructor)
+
+ // Take the result of calling `mFunction`.
+ // Precondition: mReturnValue.has_value()
+ // Postcondition: !mReturnValue.has_value()
+ [[nodiscard]] ReturnType take();
+
+ private:
+ std::optional<ReturnType> mReturnValue;
+ FunctionType mFunction;
+};
+
+// Deduction guidelines for CallbackValue when constructed with a function pointer.
+template <typename ReturnType, typename... ArgTypes>
+CallbackValue(ReturnType (*)(ArgTypes...))->CallbackValue<ReturnType, ArgTypes...>;
+
+// Thread-safe container to pass a value between threads.
template <typename Type>
class TransferValue final {
public:
+ // Put the value in `TransferValue`. If `TransferValue` already has a value, this function is a
+ // no-op.
void put(Type object) const;
+
+ // Take the value stored in `TransferValue`. If no value is available, this function will block
+ // until the value becomes available.
+ // Postcondition: !mObject.has_value()
[[nodiscard]] Type take() const;
private:
@@ -38,7 +79,23 @@
mutable std::optional<Type> mObject GUARDED_BY(mMutex);
};
-// template implementation
+// template implementations
+
+template <typename ReturnType, typename... ArgTypes>
+CallbackValue<ReturnType, ArgTypes...>::CallbackValue(FunctionType fn) : mFunction(fn) {}
+
+template <typename ReturnType, typename... ArgTypes>
+CallbackValue<ReturnType, ArgTypes...>::operator CallbackType() {
+ return [this](ArgTypes... args) { mReturnValue = mFunction(args...); };
+}
+
+template <typename ReturnType, typename... ArgTypes>
+ReturnType CallbackValue<ReturnType, ArgTypes...>::take() {
+ CHECK(mReturnValue.has_value());
+ std::optional<ReturnType> object;
+ std::swap(object, mReturnValue);
+ return std::move(object).value();
+}
template <typename Type>
void TransferValue<Type>::put(Type object) const {
@@ -56,6 +113,7 @@
std::unique_lock lock(mMutex);
base::ScopedLockAssertion lockAssertion(mMutex);
mCondition.wait(lock, [this]() REQUIRES(mMutex) { return mObject.has_value(); });
+ CHECK(mObject.has_value());
std::optional<Type> object;
std::swap(object, mObject);
return std::move(object).value();
diff --git a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
index 9ae7a63..a46f4ac 100644
--- a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
+++ b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
@@ -29,7 +29,7 @@
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
InvalidPreparedModel::execute(const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
const nn::OptionalTimePoint& /*deadline*/,
- const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/) const {
+ const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
return NN_ERROR() << "InvalidPreparedModel";
}
@@ -37,8 +37,8 @@
InvalidPreparedModel::executeFenced(
const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/,
nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/,
- const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
- const nn::OptionalTimeoutDuration& /*timeoutDurationAfterFence*/) const {
+ const nn::OptionalDuration& /*loopTimeoutDuration*/,
+ const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
return NN_ERROR() << "InvalidPreparedModel";
}
diff --git a/neuralnetworks/utils/common/src/ResilientBuffer.cpp b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
index 984295b..47abbe2 100644
--- a/neuralnetworks/utils/common/src/ResilientBuffer.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
@@ -20,6 +20,7 @@
#include <android-base/thread_annotations.h>
#include <nnapi/IBuffer.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <functional>
@@ -29,6 +30,34 @@
#include <vector>
namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientBuffer& resilientBuffer, const FnType& fn)
+ -> decltype(fn(*resilientBuffer.getBuffer())) {
+ auto buffer = resilientBuffer.getBuffer();
+ auto result = fn(*buffer);
+
+ // Immediately return if device is not dead.
+ if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+ return result;
+ }
+
+ // Attempt recovery and return if it fails.
+ auto maybeBuffer = resilientBuffer.recover(buffer.get());
+ if (!maybeBuffer.has_value()) {
+ const auto& [resultErrorMessage, resultErrorCode] = result.error();
+ const auto& [recoveryErrorMessage, recoveryErrorCode] = maybeBuffer.error();
+ return nn::error(resultErrorCode)
+ << resultErrorMessage << ", and failed to recover dead buffer with error "
+ << recoveryErrorCode << ": " << recoveryErrorMessage;
+ }
+ buffer = std::move(maybeBuffer).value();
+
+ return fn(*buffer);
+}
+
+} // namespace
nn::GeneralResult<std::shared_ptr<const ResilientBuffer>> ResilientBuffer::create(
Factory makeBuffer) {
@@ -36,7 +65,7 @@
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
<< "utils::ResilientBuffer::create must have non-empty makeBuffer";
}
- auto buffer = NN_TRY(makeBuffer(/*blocking=*/true));
+ auto buffer = NN_TRY(makeBuffer());
CHECK(buffer != nullptr);
return std::make_shared<const ResilientBuffer>(PrivateConstructorTag{}, std::move(makeBuffer),
std::move(buffer));
@@ -53,9 +82,16 @@
std::lock_guard guard(mMutex);
return mBuffer;
}
-nn::SharedBuffer ResilientBuffer::recover(const nn::IBuffer* /*failingBuffer*/,
- bool /*blocking*/) const {
+nn::GeneralResult<nn::SharedBuffer> ResilientBuffer::recover(
+ const nn::IBuffer* failingBuffer) const {
std::lock_guard guard(mMutex);
+
+ // Another caller updated the failing prepared model.
+ if (mBuffer.get() != failingBuffer) {
+ return mBuffer;
+ }
+
+ mBuffer = NN_TRY(kMakeBuffer());
return mBuffer;
}
@@ -64,12 +100,16 @@
}
nn::GeneralResult<void> ResilientBuffer::copyTo(const nn::Memory& dst) const {
- return getBuffer()->copyTo(dst);
+ const auto fn = [&dst](const nn::IBuffer& buffer) { return buffer.copyTo(dst); };
+ return protect(*this, fn);
}
nn::GeneralResult<void> ResilientBuffer::copyFrom(const nn::Memory& src,
const nn::Dimensions& dimensions) const {
- return getBuffer()->copyFrom(src, dimensions);
+ const auto fn = [&src, &dimensions](const nn::IBuffer& buffer) {
+ return buffer.copyFrom(src, dimensions);
+ };
+ return protect(*this, fn);
}
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientDevice.cpp b/neuralnetworks/utils/common/src/ResilientDevice.cpp
index 2f83c5c..2023c9a 100644
--- a/neuralnetworks/utils/common/src/ResilientDevice.cpp
+++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp
@@ -49,7 +49,17 @@
return result;
}
- device = resilientDevice.recover(device.get(), blocking);
+ // Attempt recovery and return if it fails.
+ auto maybeDevice = resilientDevice.recover(device.get(), blocking);
+ if (!maybeDevice.has_value()) {
+ const auto& [resultErrorMessage, resultErrorCode] = result.error();
+ const auto& [recoveryErrorMessage, recoveryErrorCode] = maybeDevice.error();
+ return nn::error(resultErrorCode)
+ << resultErrorMessage << ", and failed to recover dead device with error "
+ << recoveryErrorCode << ": " << recoveryErrorMessage;
+ }
+ device = std::move(maybeDevice).value();
+
return fn(*device);
}
@@ -94,7 +104,8 @@
return mDevice;
}
-nn::SharedDevice ResilientDevice::recover(const nn::IDevice* failingDevice, bool blocking) const {
+nn::GeneralResult<nn::SharedDevice> ResilientDevice::recover(const nn::IDevice* failingDevice,
+ bool blocking) const {
std::lock_guard guard(mMutex);
// Another caller updated the failing device.
@@ -102,13 +113,7 @@
return mDevice;
}
- auto maybeDevice = kMakeDevice(blocking);
- if (!maybeDevice.has_value()) {
- const auto& [message, code] = maybeDevice.error();
- LOG(ERROR) << "Failed to recover dead device with error " << code << ": " << message;
- return mDevice;
- }
- auto device = std::move(maybeDevice).value();
+ auto device = NN_TRY(kMakeDevice(blocking));
// If recovered device has different metadata than what is cached (i.e., because it was
// updated), mark the device as invalid and preserve the cached data.
@@ -175,40 +180,50 @@
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 {
+#if 0
auto self = shared_from_this();
- ResilientPreparedModel::Factory makePreparedModel =
- [device = std::move(self), model, preference, priority, deadline, modelCache, dataCache,
- token](bool blocking) -> nn::GeneralResult<nn::SharedPreparedModel> {
- return device->prepareModelInternal(blocking, model, preference, priority, deadline,
- modelCache, dataCache, token);
+ ResilientPreparedModel::Factory makePreparedModel = [device = std::move(self), model,
+ preference, priority, deadline, modelCache,
+ dataCache, token] {
+ return device->prepareModelInternal(model, preference, priority, deadline, modelCache,
+ dataCache, token);
};
return ResilientPreparedModel::create(std::move(makePreparedModel));
+#else
+ return prepareModelInternal(model, preference, priority, deadline, modelCache, dataCache,
+ token);
+#endif
}
nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelFromCache(
nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+#if 0
auto self = shared_from_this();
- ResilientPreparedModel::Factory makePreparedModel =
- [device = std::move(self), deadline, modelCache, dataCache,
- token](bool blocking) -> nn::GeneralResult<nn::SharedPreparedModel> {
- return device->prepareModelFromCacheInternal(blocking, deadline, modelCache, dataCache,
- token);
+ ResilientPreparedModel::Factory makePreparedModel = [device = std::move(self), deadline,
+ modelCache, dataCache, token] {
+ return device->prepareModelFromCacheInternal(deadline, modelCache, dataCache, token);
};
return ResilientPreparedModel::create(std::move(makePreparedModel));
+#else
+ return prepareModelFromCacheInternal(deadline, modelCache, dataCache, token);
+#endif
}
nn::GeneralResult<nn::SharedBuffer> ResilientDevice::allocate(
const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
const std::vector<nn::BufferRole>& inputRoles,
const std::vector<nn::BufferRole>& outputRoles) const {
+#if 0
auto self = shared_from_this();
- ResilientBuffer::Factory makeBuffer =
- [device = std::move(self), desc, preparedModels, inputRoles,
- outputRoles](bool blocking) -> nn::GeneralResult<nn::SharedBuffer> {
- return device->allocateInternal(blocking, desc, preparedModels, inputRoles, outputRoles);
+ ResilientBuffer::Factory makeBuffer = [device = std::move(self), desc, preparedModels,
+ inputRoles, outputRoles] {
+ return device->allocateInternal(desc, preparedModels, inputRoles, outputRoles);
};
return ResilientBuffer::create(std::move(makeBuffer));
+#else
+ return allocateInternal(desc, preparedModels, inputRoles, outputRoles);
+#endif
}
bool ResilientDevice::isValidInternal() const {
@@ -217,37 +232,34 @@
}
nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelInternal(
- bool blocking, const nn::Model& model, nn::ExecutionPreference preference,
- nn::Priority priority, nn::OptionalTimePoint deadline,
- const std::vector<nn::SharedHandle>& modelCache,
+ 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 {
if (!isValidInternal()) {
return std::make_shared<const InvalidPreparedModel>();
}
- const auto fn = [&model, preference, priority, deadline, &modelCache, &dataCache,
- token](const nn::IDevice& device) {
+ const auto fn = [&model, preference, priority, &deadline, &modelCache, &dataCache,
+ &token](const nn::IDevice& device) {
return device.prepareModel(model, preference, priority, deadline, modelCache, dataCache,
token);
};
- return protect(*this, fn, blocking);
+ return protect(*this, fn, /*blocking=*/false);
}
nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelFromCacheInternal(
- bool blocking, nn::OptionalTimePoint deadline,
- const std::vector<nn::SharedHandle>& modelCache,
+ nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
if (!isValidInternal()) {
return std::make_shared<const InvalidPreparedModel>();
}
- const auto fn = [deadline, &modelCache, &dataCache, token](const nn::IDevice& device) {
+ const auto fn = [&deadline, &modelCache, &dataCache, &token](const nn::IDevice& device) {
return device.prepareModelFromCache(deadline, modelCache, dataCache, token);
};
- return protect(*this, fn, blocking);
+ return protect(*this, fn, /*blocking=*/false);
}
nn::GeneralResult<nn::SharedBuffer> ResilientDevice::allocateInternal(
- bool blocking, const nn::BufferDesc& desc,
- const std::vector<nn::SharedPreparedModel>& preparedModels,
+ const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
const std::vector<nn::BufferRole>& inputRoles,
const std::vector<nn::BufferRole>& outputRoles) const {
if (!isValidInternal()) {
@@ -256,7 +268,7 @@
const auto fn = [&desc, &preparedModels, &inputRoles, &outputRoles](const nn::IDevice& device) {
return device.allocate(desc, preparedModels, inputRoles, outputRoles);
};
- return protect(*this, fn, blocking);
+ return protect(*this, fn, /*blocking=*/false);
}
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
index 1c9ecba..667df2b 100644
--- a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
+++ b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
@@ -20,15 +20,45 @@
#include <android-base/thread_annotations.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <functional>
#include <memory>
#include <mutex>
+#include <sstream>
#include <utility>
#include <vector>
namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientPreparedModel& resilientPreparedModel, const FnType& fn)
+ -> decltype(fn(*resilientPreparedModel.getPreparedModel())) {
+ auto preparedModel = resilientPreparedModel.getPreparedModel();
+ auto result = fn(*preparedModel);
+
+ // Immediately return if prepared model is not dead.
+ if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+ return result;
+ }
+
+ // Attempt recovery and return if it fails.
+ auto maybePreparedModel = resilientPreparedModel.recover(preparedModel.get());
+ if (!maybePreparedModel.has_value()) {
+ const auto& [message, code] = maybePreparedModel.error();
+ std::ostringstream oss;
+ oss << ", and failed to recover dead prepared model with error " << code << ": " << message;
+ result.error().message += oss.str();
+ return result;
+ }
+ preparedModel = std::move(maybePreparedModel).value();
+
+ return fn(*preparedModel);
+}
+
+} // namespace
nn::GeneralResult<std::shared_ptr<const ResilientPreparedModel>> ResilientPreparedModel::create(
Factory makePreparedModel) {
@@ -36,7 +66,7 @@
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
<< "utils::ResilientPreparedModel::create must have non-empty makePreparedModel";
}
- auto preparedModel = NN_TRY(makePreparedModel(/*blocking=*/true));
+ auto preparedModel = NN_TRY(makePreparedModel());
CHECK(preparedModel != nullptr);
return std::make_shared<ResilientPreparedModel>(
PrivateConstructorTag{}, std::move(makePreparedModel), std::move(preparedModel));
@@ -55,27 +85,43 @@
return mPreparedModel;
}
-nn::SharedPreparedModel ResilientPreparedModel::recover(
- const nn::IPreparedModel* /*failingPreparedModel*/, bool /*blocking*/) const {
+nn::GeneralResult<nn::SharedPreparedModel> ResilientPreparedModel::recover(
+ const nn::IPreparedModel* failingPreparedModel) const {
std::lock_guard guard(mMutex);
+
+ // Another caller updated the failing prepared model.
+ if (mPreparedModel.get() != failingPreparedModel) {
+ return mPreparedModel;
+ }
+
+ mPreparedModel = NN_TRY(kMakePreparedModel());
return mPreparedModel;
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
ResilientPreparedModel::execute(const nn::Request& request, nn::MeasureTiming measure,
const nn::OptionalTimePoint& deadline,
- const nn::OptionalTimeoutDuration& loopTimeoutDuration) const {
- return getPreparedModel()->execute(request, measure, deadline, loopTimeoutDuration);
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ const auto fn = [&request, measure, &deadline,
+ &loopTimeoutDuration](const nn::IPreparedModel& preparedModel) {
+ return preparedModel.execute(request, measure, deadline, loopTimeoutDuration);
+ };
+ return protect(*this, fn);
}
nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
-ResilientPreparedModel::executeFenced(
- const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
- nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
- const nn::OptionalTimeoutDuration& loopTimeoutDuration,
- const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const {
- return getPreparedModel()->executeFenced(request, waitFor, measure, deadline,
- loopTimeoutDuration, timeoutDurationAfterFence);
+ResilientPreparedModel::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 {
+ const auto fn = [&request, &waitFor, measure, &deadline, &loopTimeoutDuration,
+ &timeoutDurationAfterFence](const nn::IPreparedModel& preparedModel) {
+ return preparedModel.executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
+ timeoutDurationAfterFence);
+ };
+ return protect(*this, fn);
}
std::any ResilientPreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/utils/common/test/MockBuffer.h b/neuralnetworks/utils/common/test/MockBuffer.h
new file mode 100644
index 0000000..c5405fb
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockBuffer.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Types.h>
+
+namespace android::nn {
+
+class MockBuffer final : public IBuffer {
+ public:
+ MOCK_METHOD(Request::MemoryDomainToken, getToken, (), (const, override));
+ MOCK_METHOD(GeneralResult<void>, copyTo, (const Memory& dst), (const, override));
+ MOCK_METHOD(GeneralResult<void>, copyFrom, (const Memory& src, const Dimensions& dimensions),
+ (const, override));
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
diff --git a/neuralnetworks/utils/common/test/MockDevice.h b/neuralnetworks/utils/common/test/MockDevice.h
new file mode 100644
index 0000000..08cd5c5
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockDevice.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+
+namespace android::nn {
+
+class MockDevice final : public IDevice {
+ public:
+ MOCK_METHOD(const std::string&, getName, (), (const, override));
+ MOCK_METHOD(const std::string&, getVersionString, (), (const, override));
+ MOCK_METHOD(Version, getFeatureLevel, (), (const, override));
+ MOCK_METHOD(DeviceType, getType, (), (const, override));
+ MOCK_METHOD(const std::vector<Extension>&, getSupportedExtensions, (), (const, override));
+ MOCK_METHOD(const Capabilities&, getCapabilities, (), (const, override));
+ MOCK_METHOD((std::pair<uint32_t, uint32_t>), getNumberOfCacheFilesNeeded, (),
+ (const, override));
+ MOCK_METHOD(GeneralResult<void>, wait, (), (const, override));
+ MOCK_METHOD(GeneralResult<std::vector<bool>>, getSupportedOperations, (const Model& model),
+ (const, override));
+ MOCK_METHOD(GeneralResult<SharedPreparedModel>, prepareModel,
+ (const Model& model, ExecutionPreference preference, Priority priority,
+ OptionalTimePoint deadline, const std::vector<SharedHandle>& modelCache,
+ const std::vector<SharedHandle>& dataCache, const CacheToken& token),
+ (const, override));
+ MOCK_METHOD(GeneralResult<SharedPreparedModel>, prepareModelFromCache,
+ (OptionalTimePoint deadline, const std::vector<SharedHandle>& modelCache,
+ const std::vector<SharedHandle>& dataCache, const CacheToken& token),
+ (const, override));
+ MOCK_METHOD(GeneralResult<SharedBuffer>, allocate,
+ (const BufferDesc& desc, const std::vector<SharedPreparedModel>& preparedModels,
+ const std::vector<BufferRole>& inputRoles,
+ const std::vector<BufferRole>& outputRoles),
+ (const, override));
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h
new file mode 100644
index 0000000..928508e
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockPreparedModel.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IPreparedModel.h>
+
+namespace android::nn {
+
+class MockPreparedModel final : public IPreparedModel {
+ public:
+ MOCK_METHOD((ExecutionResult<std::pair<std::vector<OutputShape>, Timing>>), execute,
+ (const Request& request, MeasureTiming measure, const OptionalTimePoint& deadline,
+ const OptionalDuration& loopTimeoutDuration),
+ (const, override));
+ MOCK_METHOD((GeneralResult<std::pair<SyncFence, ExecuteFencedInfoCallback>>), executeFenced,
+ (const Request& request, const std::vector<SyncFence>& waitFor,
+ MeasureTiming measure, const OptionalTimePoint& deadline,
+ const OptionalDuration& loopTimeoutDuration,
+ const OptionalDuration& timeoutDurationAfterFence),
+ (const, override));
+ MOCK_METHOD(std::any, getUnderlyingResource, (), (const, override));
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
diff --git a/neuralnetworks/utils/common/test/ResilientBufferTest.cpp b/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
new file mode 100644
index 0000000..deb9b7c
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientBuffer.h>
+#include <tuple>
+#include <utility>
+#include "MockBuffer.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+constexpr auto kToken = nn::Request::MemoryDomainToken{1};
+
+using SharedMockBuffer = std::shared_ptr<const nn::MockBuffer>;
+using MockBufferFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedBuffer>()>;
+
+SharedMockBuffer createConfiguredMockBuffer() {
+ return std::make_shared<const nn::MockBuffer>();
+}
+
+std::tuple<std::shared_ptr<const nn::MockBuffer>, std::unique_ptr<MockBufferFactory>,
+ std::shared_ptr<const ResilientBuffer>>
+setup() {
+ auto mockBuffer = std::make_shared<const nn::MockBuffer>();
+
+ auto mockBufferFactory = std::make_unique<MockBufferFactory>();
+ EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(mockBuffer));
+
+ auto buffer = ResilientBuffer::create(mockBufferFactory->AsStdFunction()).value();
+ return std::make_tuple(std::move(mockBuffer), std::move(mockBufferFactory), std::move(buffer));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+ return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+const auto kNoError = nn::GeneralResult<void>{};
+
+} // namespace
+
+TEST(ResilientBufferTest, invalidBufferFactory) {
+ // setup call
+ const auto invalidBufferFactory = ResilientBuffer::Factory{};
+
+ // run test
+ const auto result = ResilientBuffer::create(invalidBufferFactory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientBufferTest, bufferFactoryFailure) {
+ // setup call
+ const auto invalidBufferFactory = kReturnGeneralFailure;
+
+ // run test
+ const auto result = ResilientBuffer::create(invalidBufferFactory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientBufferTest, getBuffer) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+
+ // run test
+ const auto result = buffer->getBuffer();
+
+ // verify result
+ EXPECT_TRUE(result == mockBuffer);
+}
+
+TEST(ResilientBufferTest, getToken) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, getToken()).Times(1).WillOnce(Return(kToken));
+
+ // run test
+ const auto token = buffer->getToken();
+
+ // verify result
+ EXPECT_EQ(token, kToken);
+}
+
+TEST(ResilientBufferTest, copyTo) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(Return(kNoError));
+
+ // run test
+ const auto result = buffer->copyTo({});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientBufferTest, copyToError) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = buffer->copyTo({});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientBufferTest, copyToDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = buffer->copyTo({});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientBufferTest, copyToDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnDeadObject);
+ const auto recoveredMockBuffer = createConfiguredMockBuffer();
+ EXPECT_CALL(*recoveredMockBuffer, copyTo(_)).Times(1).WillOnce(Return(kNoError));
+ EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
+
+ // run test
+ const auto result = buffer->copyTo({});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientBufferTest, copyFrom) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(Return(kNoError));
+
+ // run test
+ const auto result = buffer->copyFrom({}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientBufferTest, copyFromError) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = buffer->copyFrom({}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientBufferTest, copyFromDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = buffer->copyFrom({}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientBufferTest, copyFromDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnDeadObject);
+ const auto recoveredMockBuffer = createConfiguredMockBuffer();
+ EXPECT_CALL(*recoveredMockBuffer, copyFrom(_, _)).Times(1).WillOnce(Return(kNoError));
+ EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
+
+ // run test
+ const auto result = buffer->copyFrom({}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientBufferTest, recover) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ const auto recoveredMockBuffer = createConfiguredMockBuffer();
+ EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
+
+ // run test
+ const auto result = buffer->recover(mockBuffer.get());
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() == recoveredMockBuffer);
+}
+
+TEST(ResilientBufferTest, recoverFailure) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ const auto recoveredMockBuffer = createConfiguredMockBuffer();
+ EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = buffer->recover(mockBuffer.get());
+
+ // verify result
+ EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientBufferTest, someoneElseRecovered) {
+ // setup call
+ const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+ const auto recoveredMockBuffer = createConfiguredMockBuffer();
+ EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
+ buffer->recover(mockBuffer.get());
+
+ // run test
+ const auto result = buffer->recover(mockBuffer.get());
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() == recoveredMockBuffer);
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp b/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp
new file mode 100644
index 0000000..3abd724
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp
@@ -0,0 +1,725 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <tuple>
+#include <utility>
+#include "MockBuffer.h"
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+using SharedMockDevice = std::shared_ptr<const nn::MockDevice>;
+using MockDeviceFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedDevice>(bool)>;
+
+const std::string kName = "Google-MockV1";
+const std::string kVersionString = "version1";
+const auto kExtensions = std::vector<nn::Extension>{};
+constexpr auto kNoInfo = std::numeric_limits<float>::max();
+constexpr auto kNoPerformanceInfo =
+ nn::Capabilities::PerformanceInfo{.execTime = kNoInfo, .powerUsage = kNoInfo};
+const auto kCapabilities = nn::Capabilities{
+ .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+ .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+ .operandPerformance = nn::Capabilities::OperandPerformanceTable::create({}).value(),
+ .ifPerformance = kNoPerformanceInfo,
+ .whilePerformance = kNoPerformanceInfo};
+constexpr auto kNumberOfCacheFilesNeeded = std::pair<uint32_t, uint32_t>(5, 3);
+
+SharedMockDevice createConfiguredMockDevice() {
+ auto mockDevice = std::make_shared<const nn::MockDevice>();
+
+ // Setup default actions for each relevant call.
+ constexpr auto getName_ret = []() -> const std::string& { return kName; };
+ constexpr auto getVersionString_ret = []() -> const std::string& { return kVersionString; };
+ constexpr auto kFeatureLevel = nn::Version::ANDROID_OC_MR1;
+ constexpr auto kDeviceType = nn::DeviceType::ACCELERATOR;
+ constexpr auto getSupportedExtensions_ret = []() -> const std::vector<nn::Extension>& {
+ return kExtensions;
+ };
+ constexpr auto getCapabilities_ret = []() -> const nn::Capabilities& { return kCapabilities; };
+
+ // Setup default actions for each relevant call.
+ ON_CALL(*mockDevice, getName()).WillByDefault(getName_ret);
+ ON_CALL(*mockDevice, getVersionString()).WillByDefault(getVersionString_ret);
+ ON_CALL(*mockDevice, getFeatureLevel()).WillByDefault(Return(kFeatureLevel));
+ ON_CALL(*mockDevice, getType()).WillByDefault(Return(kDeviceType));
+ ON_CALL(*mockDevice, getSupportedExtensions()).WillByDefault(getSupportedExtensions_ret);
+ ON_CALL(*mockDevice, getCapabilities()).WillByDefault(getCapabilities_ret);
+ ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded())
+ .WillByDefault(Return(kNumberOfCacheFilesNeeded));
+
+ // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+ // uninteresting methods calls.
+ EXPECT_CALL(*mockDevice, getName()).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getVersionString()).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getFeatureLevel()).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getType()).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getSupportedExtensions()).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getCapabilities()).Times(testing::AnyNumber());
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded()).Times(testing::AnyNumber());
+
+ return mockDevice;
+}
+
+std::tuple<SharedMockDevice, std::unique_ptr<MockDeviceFactory>,
+ std::shared_ptr<const ResilientDevice>>
+setup() {
+ auto mockDevice = createConfiguredMockDevice();
+
+ auto mockDeviceFactory = std::make_unique<MockDeviceFactory>();
+ EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(Return(mockDevice));
+
+ auto device = ResilientDevice::create(mockDeviceFactory->AsStdFunction()).value();
+ return std::make_tuple(std::move(mockDevice), std::move(mockDeviceFactory), std::move(device));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+ return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+} // namespace
+
+TEST(ResilientDeviceTest, invalidDeviceFactory) {
+ // setup call
+ const auto invalidDeviceFactory = ResilientDevice::Factory{};
+
+ // run test
+ const auto result = ResilientDevice::create(invalidDeviceFactory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientDeviceTest, preparedModelFactoryFailure) {
+ // setup call
+ const auto invalidDeviceFactory = kReturnGeneralFailure;
+
+ // run test
+ const auto result = ResilientDevice::create(invalidDeviceFactory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, cachedData) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+
+ // run test and verify results
+ EXPECT_EQ(device->getName(), kName);
+ EXPECT_EQ(device->getVersionString(), kVersionString);
+ EXPECT_EQ(device->getSupportedExtensions(), kExtensions);
+ EXPECT_EQ(device->getCapabilities(), kCapabilities);
+}
+
+TEST(ResilientDeviceTest, getFeatureLevel) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ constexpr auto kFeatureLevel = nn::Version::ANDROID_OC_MR1;
+ EXPECT_CALL(*mockDevice, getFeatureLevel()).Times(1).WillOnce(Return(kFeatureLevel));
+
+ // run test
+ const auto featureLevel = device->getFeatureLevel();
+
+ // verify results
+ EXPECT_EQ(featureLevel, kFeatureLevel);
+}
+
+TEST(ResilientDeviceTest, getType) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ constexpr auto kDeviceType = nn::DeviceType::ACCELERATOR;
+ EXPECT_CALL(*mockDevice, getType()).Times(1).WillOnce(Return(kDeviceType));
+
+ // run test
+ const auto type = device->getType();
+
+ // verify results
+ EXPECT_EQ(type, kDeviceType);
+}
+
+TEST(ResilientDeviceTest, getNumberOfCacheFilesNeeded) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded())
+ .Times(1)
+ .WillOnce(Return(kNumberOfCacheFilesNeeded));
+
+ // run test
+ const auto numberOfCacheFilesNeeded = device->getNumberOfCacheFilesNeeded();
+
+ // verify results
+ EXPECT_EQ(numberOfCacheFilesNeeded, kNumberOfCacheFilesNeeded);
+}
+
+TEST(ResilientDeviceTest, getDevice) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+
+ // run test
+ const auto result = device->getDevice();
+
+ // verify result
+ EXPECT_TRUE(result == mockDevice);
+}
+
+TEST(ResilientDeviceTest, wait) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(Return(nn::GeneralResult<void>{}));
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, waitError) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, waitDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, waitDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnDeadObject);
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*recoveredMockDevice, wait()).Times(1).WillOnce(Return(nn::GeneralResult<void>{}));
+ EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->wait();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, getSupportedOperations) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_))
+ .Times(1)
+ .WillOnce(Return(nn::GeneralResult<std::vector<bool>>{}));
+
+ // run test
+ const auto result = device->getSupportedOperations({});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, getSupportedOperationsError) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->getSupportedOperations({});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, getSupportedOperationsDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->getSupportedOperations({});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, getSupportedOperationsDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnDeadObject);
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*recoveredMockDevice, getSupportedOperations(_))
+ .Times(1)
+ .WillOnce(Return(nn::GeneralResult<std::vector<bool>>{}));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->getSupportedOperations({});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, prepareModel) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Return(mockPreparedModel));
+
+ // run test
+ const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, prepareModelError) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, prepareModelDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, prepareModelDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnDeadObject);
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+ EXPECT_CALL(*recoveredMockDevice, prepareModel(_, _, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Return(mockPreparedModel));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, prepareModelFromCache) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+ .Times(1)
+ .WillOnce(Return(mockPreparedModel));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, prepareModelFromCacheError) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, prepareModelFromCacheDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, prepareModelFromCacheDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnDeadObject);
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+ EXPECT_CALL(*recoveredMockDevice, prepareModelFromCache(_, _, _, _))
+ .Times(1)
+ .WillOnce(Return(mockPreparedModel));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, allocate) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto mockBuffer = std::make_shared<const nn::MockBuffer>();
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(Return(mockBuffer));
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, allocateError) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, allocateDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, allocateDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ const auto mockBuffer = std::make_shared<const nn::MockBuffer>();
+ EXPECT_CALL(*recoveredMockDevice, allocate(_, _, _, _)).Times(1).WillOnce(Return(mockBuffer));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, recover) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() == recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverFailure) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*mockDeviceFactory, Call(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientDeviceTest, someoneElseRecovered) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+ device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() == recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetName) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ const std::string kDifferentName = "Google-DifferentName";
+ const auto ret = [&kDifferentName]() -> const std::string& { return kDifferentName; };
+ EXPECT_CALL(*recoveredMockDevice, getName()).Times(1).WillOnce(ret);
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+ EXPECT_TRUE(result.value() != mockDevice);
+ EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetVersionString) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ const std::string kDifferentVersionString = "differentversion";
+ const auto ret = [&kDifferentVersionString]() -> const std::string& {
+ return kDifferentVersionString;
+ };
+ EXPECT_CALL(*recoveredMockDevice, getVersionString()).Times(1).WillOnce(ret);
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+ EXPECT_TRUE(result.value() != mockDevice);
+ EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetFeatureLevel) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*recoveredMockDevice, getFeatureLevel())
+ .Times(1)
+ .WillOnce(Return(nn::Version::ANDROID_P));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+ EXPECT_TRUE(result.value() != mockDevice);
+ EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetType) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+ EXPECT_TRUE(result.value() != mockDevice);
+ EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetSupportedExtensions) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ const auto kDifferentExtensions =
+ std::vector<nn::Extension>{nn::Extension{.name = "", .operandTypes = {}}};
+ const auto ret = [&kDifferentExtensions]() -> const std::vector<nn::Extension>& {
+ return kDifferentExtensions;
+ };
+ EXPECT_CALL(*recoveredMockDevice, getSupportedExtensions()).Times(1).WillOnce(ret);
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+ EXPECT_TRUE(result.value() != mockDevice);
+ EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetCapabilities) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ const auto kDifferentCapabilities = nn::Capabilities{
+ .relaxedFloat32toFloat16PerformanceTensor = {.execTime = 0.5f, .powerUsage = 0.5f},
+ .operandPerformance = nn::Capabilities::OperandPerformanceTable::create({}).value()};
+ const auto ret = [&kDifferentCapabilities]() -> const nn::Capabilities& {
+ return kDifferentCapabilities;
+ };
+ EXPECT_CALL(*recoveredMockDevice, getCapabilities()).Times(1).WillOnce(ret);
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+ // run test
+ const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+ EXPECT_TRUE(result.value() != mockDevice);
+ EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchInvalidPrepareModel) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+ device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // run test
+ auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchInvalidPrepareModelFromCache) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+ device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // run test
+ auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchInvalidAllocate) {
+ // setup call
+ const auto [mockDevice, mockDeviceFactory, device] = setup();
+ const auto recoveredMockDevice = createConfiguredMockDevice();
+ EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
+ EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+ device->recover(mockDevice.get(), /*blocking=*/false);
+
+ // run test
+ auto result = device->allocate({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() != nullptr);
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
new file mode 100644
index 0000000..6d86e10
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientPreparedModel.h>
+#include <utility>
+#include "MockPreparedModel.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+using SharedMockPreparedModel = std::shared_ptr<const nn::MockPreparedModel>;
+using MockPreparedModelFactory =
+ ::testing::MockFunction<nn::GeneralResult<nn::SharedPreparedModel>()>;
+
+SharedMockPreparedModel createConfiguredMockPreparedModel() {
+ return std::make_shared<const nn::MockPreparedModel>();
+}
+
+std::tuple<std::shared_ptr<const nn::MockPreparedModel>, std::unique_ptr<MockPreparedModelFactory>,
+ std::shared_ptr<const ResilientPreparedModel>>
+setup() {
+ auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+
+ auto mockPreparedModelFactory = std::make_unique<MockPreparedModelFactory>();
+ EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(Return(mockPreparedModel));
+
+ auto buffer = ResilientPreparedModel::create(mockPreparedModelFactory->AsStdFunction()).value();
+ return std::make_tuple(std::move(mockPreparedModel), std::move(mockPreparedModelFactory),
+ std::move(buffer));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+ return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+const auto kNoExecutionError =
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>{};
+const auto kNoFencedExecutionError =
+ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>(
+ std::make_pair(nn::SyncFence::createAsSignaled(), nullptr));
+
+struct FakeResource {};
+
+} // namespace
+
+TEST(ResilientPreparedModelTest, invalidPreparedModelFactory) {
+ // setup call
+ const auto invalidPreparedModelFactory = ResilientPreparedModel::Factory{};
+
+ // run test
+ const auto result = ResilientPreparedModel::create(invalidPreparedModelFactory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientPreparedModelTest, preparedModelFactoryFailure) {
+ // setup call
+ const auto invalidPreparedModelFactory = kReturnGeneralFailure;
+
+ // run test
+ const auto result = ResilientPreparedModel::create(invalidPreparedModelFactory);
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientPreparedModelTest, getPreparedModel) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+
+ // run test
+ const auto result = preparedModel->getPreparedModel();
+
+ // verify result
+ EXPECT_TRUE(result == mockPreparedModel);
+}
+
+TEST(ResilientPreparedModelTest, execute) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _))
+ .Times(1)
+ .WillOnce(Return(kNoExecutionError));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, executeError) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientPreparedModelTest, executeDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
+ constexpr auto ret = [] { return nn::error(nn::ErrorStatus::GENERAL_FAILURE); };
+ EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(ret);
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientPreparedModelTest, executeDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
+ const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+ EXPECT_CALL(*recoveredMockPreparedModel, execute(_, _, _, _))
+ .Times(1)
+ .WillOnce(Return(kNoExecutionError));
+ EXPECT_CALL(*mockPreparedModelFactory, Call())
+ .Times(1)
+ .WillOnce(Return(recoveredMockPreparedModel));
+
+ // run test
+ const auto result = preparedModel->execute({}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, executeFenced) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Return(kNoFencedExecutionError));
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, executeFencedError) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientPreparedModelTest, executeFencedDeadObjectFailedRecovery) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnDeadObject);
+ EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientPreparedModelTest, executeFencedDeadObjectSuccessfulRecovery) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(kReturnDeadObject);
+ const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+ EXPECT_CALL(*recoveredMockPreparedModel, executeFenced(_, _, _, _, _, _))
+ .Times(1)
+ .WillOnce(Return(kNoFencedExecutionError));
+ EXPECT_CALL(*mockPreparedModelFactory, Call())
+ .Times(1)
+ .WillOnce(Return(recoveredMockPreparedModel));
+
+ // run test
+ const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, getUnderlyingResource) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ EXPECT_CALL(*mockPreparedModel, getUnderlyingResource())
+ .Times(1)
+ .WillOnce(Return(FakeResource{}));
+
+ // run test
+ const auto resource = preparedModel->getUnderlyingResource();
+
+ // verify resource
+ const FakeResource* maybeFakeResource = std::any_cast<FakeResource>(&resource);
+ EXPECT_NE(maybeFakeResource, nullptr);
+}
+
+TEST(ResilientPreparedModelTest, recover) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModelFactory, Call())
+ .Times(1)
+ .WillOnce(Return(recoveredMockPreparedModel));
+
+ // run test
+ const auto result = preparedModel->recover(mockPreparedModel.get());
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() == recoveredMockPreparedModel);
+}
+
+TEST(ResilientPreparedModelTest, recoverFailure) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+ // run test
+ const auto result = preparedModel->recover(mockPreparedModel.get());
+
+ // verify result
+ EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientPreparedModelTest, someoneElseRecovered) {
+ // setup call
+ const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+ const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+ EXPECT_CALL(*mockPreparedModelFactory, Call())
+ .Times(1)
+ .WillOnce(Return(recoveredMockPreparedModel));
+ preparedModel->recover(mockPreparedModel.get());
+
+ // run test
+ const auto result = preparedModel->recover(mockPreparedModel.get());
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_TRUE(result.value() == recoveredMockPreparedModel);
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/nfc/1.0/vts/functional/AndroidTest.xml b/nfc/1.0/vts/functional/AndroidTest.xml
index 364672b..76aca48 100644
--- a/nfc/1.0/vts/functional/AndroidTest.xml
+++ b/nfc/1.0/vts/functional/AndroidTest.xml
@@ -28,6 +28,6 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalNfcV1_0TargetTest" />
- <option name="native-test-timeout" value="180000"/>
+ <option name="native-test-timeout" value="600000"/>
</test>
</configuration>
diff --git a/oemlock/aidl/Android.bp b/oemlock/aidl/Android.bp
new file mode 100644
index 0000000..bfc99e7
--- /dev/null
+++ b/oemlock/aidl/Android.bp
@@ -0,0 +1,16 @@
+aidl_interface {
+ name: "android.hardware.oemlock",
+ vendor_available: true,
+ srcs: ["android/hardware/oemlock/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/IOemLock.aidl b/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/IOemLock.aidl
new file mode 100644
index 0000000..e3c974d
--- /dev/null
+++ b/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/IOemLock.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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.oemlock;
+@VintfStability
+interface IOemLock {
+ String getName();
+ boolean isOemUnlockAllowedByCarrier();
+ boolean isOemUnlockAllowedByDevice();
+ android.hardware.oemlock.OemLockSecureStatus setOemUnlockAllowedByCarrier(in boolean allowed, in byte[] signature);
+ void setOemUnlockAllowedByDevice(in boolean allowed);
+}
diff --git a/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/OemLockSecureStatus.aidl b/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/OemLockSecureStatus.aidl
new file mode 100644
index 0000000..9d1327d
--- /dev/null
+++ b/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/OemLockSecureStatus.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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.oemlock;
+@Backing(type="int") @VintfStability
+enum OemLockSecureStatus {
+ OK = 0,
+ FAILED = 1,
+ INVALID_SIGNATURE = 2,
+}
diff --git a/oemlock/aidl/android/hardware/oemlock/IOemLock.aidl b/oemlock/aidl/android/hardware/oemlock/IOemLock.aidl
new file mode 100644
index 0000000..674ff85
--- /dev/null
+++ b/oemlock/aidl/android/hardware/oemlock/IOemLock.aidl
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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.oemlock;
+
+import android.hardware.oemlock.OemLockSecureStatus;
+
+/*
+ * The OEM lock prevents the bootloader from allowing the device to be flashed.
+ *
+ * Both the carrier and the device itself have a say as to whether OEM unlock is
+ * allowed and both must agree that is allowed in order for unlock to be
+ * possible.
+ */
+@VintfStability
+interface IOemLock {
+ /**
+ * Returns a vendor specific identifier of the HAL.
+ *
+ * The name returned must not be interpreted by the framework but must be
+ * passed to vendor code which may use it to identify the security protocol
+ * used by setOemUnlockAllowedByCarrier. This allows the vendor to identify
+ * the protocol without having to maintain a device-to-protocol mapping.
+ *
+ * @return name of the implementation and STATUS_OK if get name successfully
+ */
+ String getName();
+
+ /**
+ * Returns whether OEM unlock is allowed by the carrier.
+ *
+ * @return the current state(allowed/not allowed) of the flag
+ * and STATUS_OK if the flag was successfully read.
+ */
+ boolean isOemUnlockAllowedByCarrier();
+
+ /**
+ * Returns whether OEM unlock ia allowed by the device.
+ *
+ * @return the current state(allowed/not allowed) of the flag
+ * and STATUS_OK if the flag was successfully read.
+ */
+ boolean isOemUnlockAllowedByDevice();
+
+ /**
+ * Updates whether OEM unlock is allowed by the carrier.
+ *
+ * The implementation may require a vendor defined signature to prove the
+ * validity of this request in order to harden its security.
+ *
+ * @param allowed is the new value of the flag.
+ * @param signature to prove validity of this request or empty if not
+ * required.
+ * @return OK if the flag was successfully updated,
+ * INVALID_SIGNATURE if a signature is required but the wrong one
+ * was provided
+ * FAILED if the update was otherwise unsuccessful.
+ */
+ OemLockSecureStatus setOemUnlockAllowedByCarrier(in boolean allowed, in byte[] signature);
+
+ /**
+ * Updates whether OEM unlock is allowed by the device.
+ *
+ * @param allowed the new value of the flag.
+ * @return STATUS_OK if the flag was successfully updated.
+ */
+ void setOemUnlockAllowedByDevice(in boolean allowed);
+}
diff --git a/keymint/aidl/android/hardware/keymint/Certificate.aidl b/oemlock/aidl/android/hardware/oemlock/OemLockSecureStatus.aidl
similarity index 66%
copy from keymint/aidl/android/hardware/keymint/Certificate.aidl
copy to oemlock/aidl/android/hardware/oemlock/OemLockSecureStatus.aidl
index 3a70970..3c11377 100644
--- a/keymint/aidl/android/hardware/keymint/Certificate.aidl
+++ b/oemlock/aidl/android/hardware/oemlock/OemLockSecureStatus.aidl
@@ -14,16 +14,21 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
-/**
- * This encodes the IKeyMintDevice attestation generated certificate.
- */
+package android.hardware.oemlock;
@VintfStability
-parcelable Certificate {
+@Backing(type="int")
+enum OemLockSecureStatus {
/**
- * EncodedCertificate contains the bytes of a DER-encoded X.509 certificate.
+ * The operation completed successfully.
*/
- byte[] encodedCertificate;
+ OK,
+ /**
+ * The operation encountered a problem.
+ */
+ FAILED,
+ /**
+ * An invalid signature was provided so the operation was not performed.
+ */
+ INVALID_SIGNATURE,
}
diff --git a/keymint/support/Android.bp b/oemlock/aidl/default/Android.bp
similarity index 64%
copy from keymint/support/Android.bp
copy to oemlock/aidl/default/Android.bp
index 432416e..b9872d7 100644
--- a/keymint/support/Android.bp
+++ b/oemlock/aidl/default/Android.bp
@@ -14,26 +14,19 @@
// limitations under the License.
//
-cc_library {
- name: "libkeymintSupport",
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
+cc_binary {
+ name: "android.hardware.oemlock-service.example",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.oemlock-service.example.rc"],
+ vintf_fragments: ["android.hardware.oemlock-service.example.xml"],
+ vendor: true,
srcs: [
- "attestation_record.cpp",
- "authorization_set.cpp",
- "keymint_utils.cpp",
- "key_param_output.cpp",
- ],
- export_include_dirs: [
- "include",
+ "service.cpp",
+ "OemLock.cpp",
],
shared_libs: [
- "android.hardware.keymint-cpp",
+ "android.hardware.oemlock-ndk_platform",
"libbase",
- "libcrypto",
- "libutils",
+ "libbinder_ndk",
],
}
diff --git a/oemlock/aidl/default/OemLock.cpp b/oemlock/aidl/default/OemLock.cpp
new file mode 100644
index 0000000..646b532
--- /dev/null
+++ b/oemlock/aidl/default/OemLock.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#include "OemLock.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace oemlock {
+
+// Methods from ::android::hardware::oemlock::IOemLock follow.
+
+::ndk::ScopedAStatus OemLock::getName(std::string *out_name) {
+ (void)out_name;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus OemLock::setOemUnlockAllowedByCarrier(bool in_allowed, const std::vector<uint8_t> &in_signature, OemLockSecureStatus *_aidl_return) {
+ (void)in_allowed;
+ (void)in_signature;
+ (void)_aidl_return;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus OemLock::isOemUnlockAllowedByCarrier(bool *out_allowed) {
+ (void)out_allowed;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus OemLock::setOemUnlockAllowedByDevice(bool in_allowed) {
+ (void)in_allowed;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus OemLock::isOemUnlockAllowedByDevice(bool *out_allowed) {
+ (void)out_allowed;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+} // namespace oemlock
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/oemlock/aidl/default/OemLock.h b/oemlock/aidl/default/OemLock.h
new file mode 100644
index 0000000..b0df414
--- /dev/null
+++ b/oemlock/aidl/default/OemLock.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/oemlock/BnOemLock.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace oemlock {
+
+using ::aidl::android::hardware::oemlock::IOemLock;
+using ::aidl::android::hardware::oemlock::OemLockSecureStatus;
+
+struct OemLock : public BnOemLock {
+public:
+ OemLock() = default;
+
+ // Methods from ::android::hardware::oemlock::IOemLock follow.
+ ::ndk::ScopedAStatus getName(std::string* out_name) override;
+ ::ndk::ScopedAStatus isOemUnlockAllowedByCarrier(bool* out_allowed) override;
+ ::ndk::ScopedAStatus isOemUnlockAllowedByDevice(bool* out_allowed) override;
+ ::ndk::ScopedAStatus setOemUnlockAllowedByCarrier(bool in_allowed, const std::vector<uint8_t>& in_signature, OemLockSecureStatus* _aidl_return) override;
+ ::ndk::ScopedAStatus setOemUnlockAllowedByDevice(bool in_allowed) override;
+};
+
+} // namespace oemlock
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/oemlock/aidl/default/android.hardware.oemlock-service.example.rc b/oemlock/aidl/default/android.hardware.oemlock-service.example.rc
new file mode 100644
index 0000000..57b79d3
--- /dev/null
+++ b/oemlock/aidl/default/android.hardware.oemlock-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.oemlock_default /vendor/bin/hw/android.hardware.oemlock-service.example
+ class hal
+ user hsm
+ group hsm
diff --git a/oemlock/aidl/default/android.hardware.oemlock-service.example.xml b/oemlock/aidl/default/android.hardware.oemlock-service.example.xml
new file mode 100644
index 0000000..b9f137f
--- /dev/null
+++ b/oemlock/aidl/default/android.hardware.oemlock-service.example.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.oemlock</name>
+ <interface>
+ <name>IOemLock</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/oemlock/aidl/default/service.cpp b/oemlock/aidl/default/service.cpp
new file mode 100644
index 0000000..af828a0
--- /dev/null
+++ b/oemlock/aidl/default/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "OemLock.h"
+
+using ::aidl::android::hardware::oemlock::OemLock;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<OemLock> oemlock = ndk::SharedRefBase::make<OemLock>();
+
+ const std::string instance = std::string() + OemLock::descriptor + "/default";
+ binder_status_t status = AServiceManager_addService(oemlock->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return -1; // Should never be reached
+}
diff --git a/keymint/support/Android.bp b/oemlock/aidl/vts/Android.bp
similarity index 63%
copy from keymint/support/Android.bp
copy to oemlock/aidl/vts/Android.bp
index 432416e..a13dbe2 100644
--- a/keymint/support/Android.bp
+++ b/oemlock/aidl/vts/Android.bp
@@ -14,26 +14,20 @@
// limitations under the License.
//
-cc_library {
- name: "libkeymintSupport",
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
+cc_test {
+ name: "VtsHalOemLockTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
],
- srcs: [
- "attestation_record.cpp",
- "authorization_set.cpp",
- "keymint_utils.cpp",
- "key_param_output.cpp",
- ],
- export_include_dirs: [
- "include",
- ],
+ srcs: ["VtsHalOemLockTargetTest.cpp"],
shared_libs: [
- "android.hardware.keymint-cpp",
+ "libbinder_ndk",
"libbase",
- "libcrypto",
- "libutils",
+ ],
+ static_libs: ["android.hardware.oemlock-ndk_platform"],
+ test_suites: [
+ "general-tests",
+ "vts",
],
}
diff --git a/oemlock/aidl/vts/VtsHalOemLockTargetTest.cpp b/oemlock/aidl/vts/VtsHalOemLockTargetTest.cpp
new file mode 100644
index 0000000..6bf6298
--- /dev/null
+++ b/oemlock/aidl/vts/VtsHalOemLockTargetTest.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/oemlock/IOemLock.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::aidl::android::hardware::oemlock::IOemLock;
+using ::aidl::android::hardware::oemlock::OemLockSecureStatus;
+
+using ndk::SpAIBinder;
+
+struct OemLockAidlTest : public ::testing::TestWithParam<std::string> {
+ virtual void SetUp() override {
+ oemlock = IOemLock::fromBinder(
+ SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+ ASSERT_NE(oemlock, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ std::shared_ptr<IOemLock> oemlock;
+};
+
+/*
+ * Check the name can be retrieved
+ */
+TEST_P(OemLockAidlTest, GetName) {
+ std::string name;
+
+ const auto ret = oemlock->getName(&name);
+
+ ASSERT_TRUE(ret.isOk());
+ // Any value acceptable
+};
+
+/*
+ * Check the unlock allowed by device state can be queried
+ */
+TEST_P(OemLockAidlTest, QueryUnlockAllowedByDevice) {
+ bool allowed;
+
+ const auto ret = oemlock->isOemUnlockAllowedByDevice(&allowed);
+
+ ASSERT_TRUE(ret.isOk());
+ // Any value acceptable
+}
+
+/*
+ * Check unlock allowed by device state can be toggled
+ */
+TEST_P(OemLockAidlTest, AllowedByDeviceCanBeToggled) {
+ bool allowed;
+
+ // Get the original state so it can be restored
+ const auto get_ret = oemlock->isOemUnlockAllowedByDevice(&allowed);
+ ASSERT_TRUE(get_ret.isOk());
+ const bool originallyAllowed = allowed;
+
+ // Toggle the state
+ const auto set_ret = oemlock->setOemUnlockAllowedByDevice(!originallyAllowed);
+ ASSERT_TRUE(set_ret.isOk());
+
+ const auto check_set_ret = oemlock->isOemUnlockAllowedByDevice(&allowed);
+ ASSERT_TRUE(check_set_ret.isOk());
+ ASSERT_EQ(allowed, !originallyAllowed);
+
+ // Restore the state
+ const auto restore_ret = oemlock->setOemUnlockAllowedByDevice(originallyAllowed);
+ ASSERT_TRUE(restore_ret.isOk());
+
+ const auto check_restore_ret = oemlock->isOemUnlockAllowedByDevice(&allowed);
+ ASSERT_TRUE(check_restore_ret.isOk());
+ ASSERT_EQ(allowed, originallyAllowed);
+}
+
+/*
+ * Check the unlock allowed by device state can be queried
+ */
+TEST_P(OemLockAidlTest, QueryUnlockAllowedByCarrier) {
+ bool allowed;
+
+ const auto ret = oemlock->isOemUnlockAllowedByCarrier(&allowed);
+
+ ASSERT_TRUE(ret.isOk());
+ // Any value acceptable
+}
+
+/*
+ * Attempt to check unlock allowed by carrier can be toggled
+ *
+ * The implementation may involve a signature which cannot be tested here. That
+ * is a valid implementation so the test will pass. If there is no signature
+ * required, the test will toggle the value.
+ */
+TEST_P(OemLockAidlTest, CarrierUnlock) {
+ const std::vector<uint8_t> noSignature = {};
+ bool allowed;
+ OemLockSecureStatus secure_status;
+
+ // Get the original state so it can be restored
+ const auto get_ret = oemlock->isOemUnlockAllowedByCarrier(&allowed);
+ ASSERT_TRUE(get_ret.isOk());
+ const bool originallyAllowed = allowed;
+
+ if (originallyAllowed) {
+ // Only applied to locked devices
+ return;
+ }
+
+ // Toggle the state
+ const auto set_ret = oemlock->setOemUnlockAllowedByCarrier(!originallyAllowed, noSignature, &secure_status);
+ ASSERT_TRUE(set_ret.isOk());
+ ASSERT_NE(secure_status, OemLockSecureStatus::FAILED);
+ const auto set_status = secure_status;
+
+ const auto check_set_ret = oemlock->isOemUnlockAllowedByCarrier(&allowed);
+ ASSERT_TRUE(check_set_ret.isOk());
+
+ if (set_status == OemLockSecureStatus::INVALID_SIGNATURE) {
+ // Signature is required so we cannot toggle the value in the test, but this is allowed
+ ASSERT_EQ(allowed, originallyAllowed);
+ return;
+ }
+
+ ASSERT_EQ(set_status, OemLockSecureStatus::OK);
+ ASSERT_EQ(allowed, !originallyAllowed);
+
+ // Restore the state
+ const auto restore_ret = oemlock->setOemUnlockAllowedByCarrier(originallyAllowed, noSignature, &secure_status);
+ ASSERT_TRUE(restore_ret.isOk());
+ ASSERT_EQ(secure_status, OemLockSecureStatus::OK);
+
+ const auto check_restore_ret = oemlock->isOemUnlockAllowedByCarrier(&allowed);
+ ASSERT_TRUE(check_restore_ret.isOk());
+ ASSERT_EQ(allowed, originallyAllowed);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OemLockAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, OemLockAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IOemLock::descriptor)),
+ android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/radio/1.0/vts/functional/vts_test_util.h b/radio/1.0/vts/functional/vts_test_util.h
index 1625f11..846148f 100644
--- a/radio/1.0/vts/functional/vts_test_util.h
+++ b/radio/1.0/vts/functional/vts_test_util.h
@@ -35,6 +35,8 @@
static constexpr const char* FEATURE_VOICE_CALL = "android.software.connectionservice";
+static constexpr const char* FEATURE_TELEPHONY = "android.hardware.telephony";
+
/*
* Generate random serial number for radio test
*/
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
index 1b254a1..4dcf1f3 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
@@ -34,6 +34,9 @@
if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
ALOGI("Skipping emergencyDial because voice call is not supported in device");
return;
+ } else if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+ ALOGI("Skipping emergencyDial because telephony radio is not supported in device");
+ return;
} else {
ALOGI("Running emergencyDial because voice call is supported in device");
}
@@ -86,6 +89,9 @@
if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
ALOGI("Skipping emergencyDial because voice call is not supported in device");
return;
+ } else if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+ ALOGI("Skipping emergencyDial because telephony radio is not supported in device");
+ return;
} else {
ALOGI("Running emergencyDial because voice call is supported in device");
}
@@ -138,6 +144,9 @@
if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
ALOGI("Skipping emergencyDial because voice call is not supported in device");
return;
+ } else if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+ ALOGI("Skipping emergencyDial because telephony radio is not supported in device");
+ return;
} else {
ALOGI("Running emergencyDial because voice call is supported in device");
}
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
index 2c8ac5e..3dc80b9 100644
--- a/radio/1.6/IRadio.hal
+++ b/radio/1.6/IRadio.hal
@@ -327,12 +327,26 @@
* @param serial Serial number of request.
* @param networkTypeBitmap a 32-bit bearer bitmap of RadioAccessFamily
*
- * Response callbask is IRadioResponse.setNetworkTypeBitmapResponse()
+ * Response callback is IRadioResponse.setNetworkTypeBitmapResponse()
*/
oneway setAllowedNetworkTypeBitmap(
uint32_t serial, bitfield<RadioAccessFamily> networkTypeBitmap);
/**
+ * Requests bitmap representing the currently allowed network types.
+ *
+ * Requests the bitmap set by the corresponding method
+ * setAllowedNetworkTypeBitmap, which sets a strict set of RATs for the
+ * radio to use. Differs from getPreferredNetworkType and getPreferredNetworkTypeBitmap
+ * in that those request *preferences*.
+ *
+ * @param serial Serial number of request.
+ *
+ * Response callback is IRadioResponse.getNetworkTypeBitmapResponse()
+ */
+ oneway getAllowedNetworkTypeBitmap(uint32_t serial);
+
+ /**
* Control data throttling at modem.
* - DataThrottlingAction:NO_DATA_THROTTLING should clear any existing
* data throttling within the requested completion window.
@@ -406,4 +420,13 @@
* Response function is IRadioResponse.getDataRegistrationStateResponse_1_6()
*/
oneway getDataRegistrationState_1_6(int32_t serial);
+
+ /**
+ * Requests current call list
+ *
+ * @param serial Serial number of request.
+ *
+ * Response function is IRadioResponse.getCurrentCallsResponse_1_6()
+ */
+ oneway getCurrentCalls_1_6(int32_t serial);
};
diff --git a/radio/1.6/IRadioResponse.hal b/radio/1.6/IRadioResponse.hal
index 3b2061f..6ac86c3 100644
--- a/radio/1.6/IRadioResponse.hal
+++ b/radio/1.6/IRadioResponse.hal
@@ -17,7 +17,9 @@
package android.hardware.radio@1.6;
import @1.0::SendSmsResult;
+import @1.4::RadioAccessFamily;
import @1.5::IRadioResponse;
+import @1.6::Call;
import @1.6::CellInfo;
import @1.6::RegStateResult;
import @1.6::RadioResponseInfo;
@@ -310,6 +312,23 @@
oneway setAllowedNetworkTypeBitmapResponse(RadioResponseInfo info);
/**
+ * Callback of IRadio.getAllowedNetworkTypeBitmap(int, bitfield<RadioAccessFamily>)
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:OPERATION_NOT_ALLOWED
+ * RadioError:MODE_NOT_SUPPORTED
+ * RadioError:INTERNAL_ERR
+ * RadioError:INVALID_ARGUMENTS
+ * RadioError:MODEM_ERR
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:NO_RESOURCES
+ */
+ oneway getAllowedNetworkTypeBitmapResponse(
+ RadioResponseInfo info, bitfield<RadioAccessFamily> networkTypeBitmap);
+
+ /**
* @param info Response info struct containing response type, serial no. and error
*
* Valid errors returned:
@@ -383,4 +402,16 @@
*/
oneway getDataRegistrationStateResponse_1_6(RadioResponseInfo info,
RegStateResult dataRegResponse);
+
+ /**
+ * @param calls Current call list
+ * RadioError:NO_MEMORY
+ * RadioError:INTERNAL_ERR
+ * RadioError:SYSTEM_ERR
+ * RadioError:INVALID_ARGUMENTS
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:NO_RESOURCES
+ * RadioError:CANCELLED
+ */
+ oneway getCurrentCallsResponse_1_6(RadioResponseInfo info, vec<Call> calls);
};
diff --git a/radio/1.6/types.hal b/radio/1.6/types.hal
index bc3df13..13b1591 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -24,6 +24,7 @@
import @1.0::RadioResponseType;
import @1.0::RegState;
import @1.1::ScanStatus;
+import @1.2::Call;
import @1.2::CellInfoCdma;
import @1.2::CellConnectionStatus;
import @1.2::TdscdmaSignalStrength;
@@ -723,3 +724,12 @@
} ngranInfo;
} accessTechnologySpecificInfo;
};
+
+struct Call {
+ @1.2::Call base;
+ /**
+ * Forwarded number. It can set only one forwarded number based on 3GPP rule of the CS.
+ * Reference: 3GPP TS 24.008 section 10.5.4.21b
+ */
+ string forwardedNumber;
+};
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 75772cd..47babed 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -406,3 +406,15 @@
EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
}
}
+
+/*
+ * Test IRadio.getCurrentCalls_1_6() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, getCurrentCalls_1_6) {
+ serial = GetRandomSerialNumber();
+ radio_v1_6->getCurrentCalls_1_6(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);
+}
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 111fcd1..fbcd7a9 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
@@ -793,6 +793,12 @@
Return<void> setAllowedNetworkTypeBitmapResponse(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+ Return<void> getAllowedNetworkTypeBitmapResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const ::android::hardware::hidl_bitfield<
+ ::android::hardware::radio::V1_4::RadioAccessFamily>
+ networkTypeBitmap);
+
Return<void> setDataThrottlingResponse(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
@@ -815,6 +821,10 @@
Return<void> getDataRegistrationStateResponse_1_6(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
const ::android::hardware::radio::V1_6::RegStateResult& regResponse);
+
+ Return<void> getCurrentCallsResponse_1_6(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::Call>& calls);
};
/* Callback class for radio indication */
diff --git a/radio/1.6/vts/functional/radio_response.cpp b/radio/1.6/vts/functional/radio_response.cpp
index 68d1f20..7c5cf6d 100644
--- a/radio/1.6/vts/functional/radio_response.cpp
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -1157,6 +1157,14 @@
return Void();
}
+Return<void> RadioResponse_v1_6::getAllowedNetworkTypeBitmapResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+ const ::android::hardware::hidl_bitfield<
+ ::android::hardware::radio::V1_4::RadioAccessFamily>
+ /*networkTypeBitmap*/) {
+ return Void();
+}
+
Return<void> RadioResponse_v1_6::setDataThrottlingResponse(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
rspInfo = info;
@@ -1199,3 +1207,11 @@
parent_v1_6.notify(info.serial);
return Void();
}
+
+Return<void> RadioResponse_v1_6::getCurrentCallsResponse_1_6(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::Call>& /*calls*/) {
+ rspInfo = info;
+ parent_v1_6.notify(info.serial);
+ return Void();
+}
diff --git a/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
similarity index 76%
rename from keymint/aidl/Android.bp
rename to security/keymint/aidl/Android.bp
index 0dae527..b5adac9 100644
--- a/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -1,8 +1,8 @@
aidl_interface {
- name: "android.hardware.keymint",
+ name: "android.hardware.security.keymint",
vendor_available: true,
srcs: [
- "android/hardware/keymint/*.aidl",
+ "android/hardware/security/keymint/*.aidl",
],
stability: "vintf",
backend: {
diff --git a/keymint/aidl/OWNERS b/security/keymint/aidl/OWNERS
similarity index 100%
rename from keymint/aidl/OWNERS
rename to security/keymint/aidl/OWNERS
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Algorithm.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
similarity index 95%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Algorithm.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
index f51a412..46e0ae0 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Algorithm.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum Algorithm {
RSA = 1,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/BeginResult.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
similarity index 86%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/BeginResult.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
index 2f56be6..ed96485 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/BeginResult.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
@@ -15,10 +15,10 @@
// 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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable BeginResult {
long challenge;
- android.hardware.keymint.KeyParameter[] params;
- android.hardware.keymint.IKeyMintOperation operation;
+ android.hardware.security.keymint.KeyParameter[] params;
+ android.hardware.security.keymint.IKeyMintOperation operation;
}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/BlockMode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
similarity index 95%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/BlockMode.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
index 94de930..dddc9d8 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/BlockMode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum BlockMode {
ECB = 1,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/ByteArray.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
similarity index 95%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/ByteArray.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
index 2dc22a9..3d18a26 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/ByteArray.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable ByteArray {
byte[] data;
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
similarity index 95%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
index ca55054..9e0f8dc 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Certificate.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable Certificate {
byte[] encodedCertificate;
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Digest.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
similarity index 96%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Digest.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
index cc4d2fd..8fc4d42 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Digest.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum Digest {
NONE = 0,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/EcCurve.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
similarity index 95%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/EcCurve.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
index 4e446ad..7c3f2f3 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/EcCurve.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum EcCurve {
P_224 = 0,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/ErrorCode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
similarity index 96%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/ErrorCode.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
index 2679243..8694b32 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum ErrorCode {
OK = 0,
@@ -94,6 +94,8 @@
ATTESTATION_IDS_NOT_PROVISIONED = -75,
INVALID_OPERATION = -76,
STORAGE_KEY_UNSUPPORTED = -77,
+ INCOMPATIBLE_MGF_DIGEST = -78,
+ UNSUPPORTED_MGF_DIGEST = -79,
UNIMPLEMENTED = -100,
VERSION_MISMATCH = -101,
UNKNOWN_ERROR = -1000,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
similarity index 86%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/HardwareAuthToken.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
index 1f5f8e9..9ea24f5 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/HardwareAuthToken.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -15,13 +15,13 @@
// 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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable HardwareAuthToken {
long challenge;
long userId;
long authenticatorId;
- android.hardware.keymint.HardwareAuthenticatorType authenticatorType;
- android.hardware.keymint.Timestamp timestamp;
+ android.hardware.security.keymint.HardwareAuthenticatorType authenticatorType;
+ android.hardware.security.keymint.Timestamp timestamp;
byte[] mac;
}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
similarity index 96%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/HardwareAuthenticatorType.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
index 95ec5c5..aef5ee0 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/HardwareAuthenticatorType.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@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
new file mode 100644
index 0000000..3d08cfe
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -0,0 +1,33 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+interface IKeyMintDevice {
+ android.hardware.security.keymint.KeyMintHardwareInfo getHardwareInfo();
+ android.hardware.security.keymint.VerificationToken verifyAuthorization(in long challenge, in android.hardware.security.keymint.HardwareAuthToken token);
+ void addRngEntropy(in byte[] data);
+ void generateKey(in android.hardware.security.keymint.KeyParameter[] keyParams, out android.hardware.security.keymint.ByteArray generatedKeyBlob, out android.hardware.security.keymint.KeyCharacteristics generatedKeyCharacteristics, out android.hardware.security.keymint.Certificate[] outCertChain);
+ void importKey(in android.hardware.security.keymint.KeyParameter[] inKeyParams, in android.hardware.security.keymint.KeyFormat inKeyFormat, in byte[] inKeyData, out android.hardware.security.keymint.ByteArray outImportedKeyBlob, out android.hardware.security.keymint.KeyCharacteristics outImportedKeyCharacteristics, out android.hardware.security.keymint.Certificate[] outCertChain);
+ void importWrappedKey(in byte[] inWrappedKeyData, in byte[] inWrappingKeyBlob, in byte[] inMaskingKey, in android.hardware.security.keymint.KeyParameter[] inUnwrappingParams, in long inPasswordSid, in long inBiometricSid, out android.hardware.security.keymint.ByteArray outImportedKeyBlob, out android.hardware.security.keymint.KeyCharacteristics outImportedKeyCharacteristics);
+ byte[] upgradeKey(in byte[] inKeyBlobToUpgrade, in android.hardware.security.keymint.KeyParameter[] inUpgradeParams);
+ void deleteKey(in byte[] inKeyBlob);
+ void deleteAllKeys();
+ void destroyAttestationIds();
+ android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose inPurpose, in byte[] inKeyBlob, in android.hardware.security.keymint.KeyParameter[] inParams, in android.hardware.security.keymint.HardwareAuthToken inAuthToken);
+ 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
new file mode 100644
index 0000000..8e3b0fc
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+interface IKeyMintOperation {
+ int update(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken inAuthToken, in @nullable android.hardware.security.keymint.VerificationToken inVerificationToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams, out @nullable android.hardware.security.keymint.ByteArray output);
+ byte[] finish(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable byte[] inSignature, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.keymint.VerificationToken inVerificationToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams);
+ void abort();
+}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyCharacteristics.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
similarity index 85%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyCharacteristics.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
index 4e73381..fb4214c 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyCharacteristics.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -15,9 +15,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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable KeyCharacteristics {
- android.hardware.keymint.KeyParameter[] softwareEnforced;
- android.hardware.keymint.KeyParameter[] hardwareEnforced;
+ android.hardware.security.keymint.KeyParameter[] softwareEnforced;
+ android.hardware.security.keymint.KeyParameter[] hardwareEnforced;
}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyFormat.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
similarity index 95%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyFormat.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
index cfa585d..f701c80 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyFormat.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum KeyFormat {
X509 = 0,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
similarity index 91%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyMintHardwareInfo.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
index 8263e60..5e9f7ae 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyMintHardwareInfo.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -15,11 +15,11 @@
// 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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable KeyMintHardwareInfo {
int versionNumber;
- android.hardware.keymint.SecurityLevel securityLevel;
+ android.hardware.security.keymint.SecurityLevel securityLevel;
@utf8InCpp String keyMintName;
@utf8InCpp String keyMintAuthorName;
}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyOrigin.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
similarity index 96%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyOrigin.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
index 8d03d2b..9728bf9 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyOrigin.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum KeyOrigin {
GENERATED = 0,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyParameter.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
similarity index 80%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyParameter.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
index 923cc68..4985768 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyParameter.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
@@ -15,12 +15,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.keymint;
-@VintfStability
+package android.hardware.security.keymint;
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
parcelable KeyParameter {
- android.hardware.keymint.Tag tag;
- boolean boolValue;
- int integer;
- long longInteger;
- byte[] blob;
+ android.hardware.security.keymint.Tag tag;
+ android.hardware.security.keymint.KeyParameterValue value;
}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyParameterArray.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterArray.aidl
similarity index 91%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyParameterArray.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterArray.aidl
index b9b9782..2c3b768 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyParameterArray.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterArray.aidl
@@ -15,8 +15,8 @@
// 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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable KeyParameterArray {
- android.hardware.keymint.KeyParameter[] params;
+ android.hardware.security.keymint.KeyParameter[] params;
}
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
new file mode 100644
index 0000000..ecf20ad
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterValue.aidl
@@ -0,0 +1,36 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+union KeyParameterValue {
+ int invalid;
+ android.hardware.security.keymint.Algorithm algorithm;
+ android.hardware.security.keymint.BlockMode blockMode;
+ android.hardware.security.keymint.PaddingMode paddingMode;
+ android.hardware.security.keymint.Digest digest;
+ android.hardware.security.keymint.EcCurve ecCurve;
+ android.hardware.security.keymint.KeyOrigin origin;
+ android.hardware.security.keymint.KeyPurpose keyPurpose;
+ android.hardware.security.keymint.HardwareAuthenticatorType hardwareAuthenticatorType;
+ android.hardware.security.keymint.SecurityLevel securityLevel;
+ boolean boolValue;
+ int integer;
+ long longInteger;
+ long dateTime;
+ byte[] blob;
+}
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyPurpose.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
similarity index 96%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyPurpose.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
index 1aee56a..a6fd8c3 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum KeyPurpose {
ENCRYPT = 0,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/PaddingMode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
similarity index 96%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/PaddingMode.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
index 97f93db..2ecfa1e 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/PaddingMode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum PaddingMode {
NONE = 1,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/SecurityLevel.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
similarity index 95%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/SecurityLevel.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
index 1fb529d..601693f 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/SecurityLevel.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum SecurityLevel {
SOFTWARE = 0,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Tag.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
similarity index 97%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Tag.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
index 33a95fe..814405c 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Tag.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum Tag {
INVALID = 0,
@@ -30,6 +30,7 @@
EC_CURVE = 268435466,
RSA_PUBLIC_EXPONENT = 1342177480,
INCLUDE_UNIQUE_ID = 1879048394,
+ RSA_OAEP_MGF_DIGEST = 536871115,
BLOB_USAGE_REQUIREMENTS = 268435757,
BOOTLOADER_ONLY = 1879048494,
ROLLBACK_RESISTANCE = 1879048495,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/TagType.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
similarity index 96%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/TagType.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
index 8214453..bb2766c 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/TagType.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@Backing(type="int") @VintfStability
enum TagType {
INVALID = 0,
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Timestamp.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
similarity index 95%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Timestamp.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
index f95d8db..4d5b659 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Timestamp.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
@@ -15,7 +15,7 @@
// 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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable Timestamp {
long milliSeconds;
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/VerificationToken.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl
similarity index 86%
rename from keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/VerificationToken.aidl
rename to security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl
index 7b4989a..5c76816 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/VerificationToken.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl
@@ -15,11 +15,11 @@
// 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.keymint;
+package android.hardware.security.keymint;
@VintfStability
parcelable VerificationToken {
long challenge;
- android.hardware.keymint.Timestamp timestamp;
- android.hardware.keymint.SecurityLevel securityLevel;
+ android.hardware.security.keymint.Timestamp timestamp;
+ android.hardware.security.keymint.SecurityLevel securityLevel;
byte[] mac;
}
diff --git a/keymint/aidl/android/hardware/keymint/Algorithm.aidl b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
similarity index 95%
rename from keymint/aidl/android/hardware/keymint/Algorithm.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
index 8c5d99c..8300b0d 100644
--- a/keymint/aidl/android/hardware/keymint/Algorithm.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.security.keymint;
/**
* Algorithms provided by IKeyMintDevice implementations.
diff --git a/keymint/aidl/android/hardware/keymint/BeginResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
similarity index 87%
rename from keymint/aidl/android/hardware/keymint/BeginResult.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
index 58eb024..aaf9f3c 100644
--- a/keymint/aidl/android/hardware/keymint/BeginResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-
-import android.hardware.keymint.IKeyMintOperation;
-import android.hardware.keymint.KeyParameter;
-
+import android.hardware.security.keymint.IKeyMintOperation;
+import android.hardware.security.keymint.KeyParameter;
/**
* This is all the results returned by the IKeyMintDevice begin() function.
diff --git a/keymint/aidl/android/hardware/keymint/BlockMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
similarity index 95%
rename from keymint/aidl/android/hardware/keymint/BlockMode.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
index b6b36cc..629c89f 100644
--- a/keymint/aidl/android/hardware/keymint/BlockMode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.security.keymint;
/**
* Symmetric block cipher modes provided by IKeyMintDevice implementations.
diff --git a/keymint/aidl/android/hardware/keymint/ByteArray.aidl b/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
similarity index 94%
rename from keymint/aidl/android/hardware/keymint/ByteArray.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
index 18d187e..c3b402e 100644
--- a/keymint/aidl/android/hardware/keymint/ByteArray.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.security.keymint;
/**
* This is used to contain a byte[], to make out parameters of byte arrays
diff --git a/keymint/aidl/android/hardware/keymint/Certificate.aidl b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
similarity index 94%
rename from keymint/aidl/android/hardware/keymint/Certificate.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
index 3a70970..a953859 100644
--- a/keymint/aidl/android/hardware/keymint/Certificate.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
/**
* This encodes the IKeyMintDevice attestation generated certificate.
diff --git a/keymint/aidl/android/hardware/keymint/Digest.aidl b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
similarity index 94%
rename from keymint/aidl/android/hardware/keymint/Digest.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
index a92ac23..b44da5a 100644
--- a/keymint/aidl/android/hardware/keymint/Digest.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.security.keymint;
/**
* Digests provided by keyMint implementations.
diff --git a/keymint/aidl/android/hardware/keymint/EcCurve.aidl b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
similarity index 94%
rename from keymint/aidl/android/hardware/keymint/EcCurve.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
index abd44b4..b9d1646 100644
--- a/keymint/aidl/android/hardware/keymint/EcCurve.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.security.keymint;
/**
* Supported EC curves, used in ECDSA
diff --git a/keymint/aidl/android/hardware/keymint/ErrorCode.aidl b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
similarity index 96%
rename from keymint/aidl/android/hardware/keymint/ErrorCode.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
index 2a54954..b20601d 100644
--- a/keymint/aidl/android/hardware/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.security.keymint;
/**
* KeyMint error codes. Aidl will return these error codes as service specific
@@ -100,6 +99,8 @@
ATTESTATION_IDS_NOT_PROVISIONED = -75,
INVALID_OPERATION = -76,
STORAGE_KEY_UNSUPPORTED = -77,
+ INCOMPATIBLE_MGF_DIGEST = -78,
+ UNSUPPORTED_MGF_DIGEST = -79,
UNIMPLEMENTED = -100,
VERSION_MISMATCH = -101,
diff --git a/keymint/aidl/android/hardware/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
similarity index 95%
rename from keymint/aidl/android/hardware/keymint/HardwareAuthToken.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
index 9b56a2e..12d615f 100644
--- a/keymint/aidl/android/hardware/keymint/HardwareAuthToken.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.Timestamp;
-import android.hardware.keymint.HardwareAuthenticatorType;
+import android.hardware.security.keymint.Timestamp;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
/**
* HardwareAuthToken is used to prove successful user authentication, to unlock the use of a key.
@@ -30,7 +30,6 @@
*/
@VintfStability
parcelable HardwareAuthToken {
-
/**
* challenge is a value that's used to enable authentication tokens to authorize specific
* events. The primary use case for challenge is to authorize an IKeyMintDevice cryptographic
diff --git a/keymint/aidl/android/hardware/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
similarity index 95%
rename from keymint/aidl/android/hardware/keymint/HardwareAuthenticatorType.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
index 5c25e2f..33f71b8 100644
--- a/keymint/aidl/android/hardware/keymint/HardwareAuthenticatorType.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
/**
* Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to
diff --git a/keymint/aidl/android/hardware/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
similarity index 98%
rename from keymint/aidl/android/hardware/keymint/IKeyMintDevice.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 8fbab79..4944acb 100644
--- a/keymint/aidl/android/hardware/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.BeginResult;
-import android.hardware.keymint.ByteArray;
-import android.hardware.keymint.Certificate;
-import android.hardware.keymint.HardwareAuthToken;
-import android.hardware.keymint.IKeyMintOperation;
-import android.hardware.keymint.KeyCharacteristics;
-import android.hardware.keymint.KeyFormat;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.KeyMintHardwareInfo;
-import android.hardware.keymint.KeyPurpose;
-import android.hardware.keymint.SecurityLevel;
-import android.hardware.keymint.VerificationToken;
+import android.hardware.security.keymint.BeginResult;
+import android.hardware.security.keymint.ByteArray;
+import android.hardware.security.keymint.Certificate;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.IKeyMintOperation;
+import android.hardware.security.keymint.KeyCharacteristics;
+import android.hardware.security.keymint.KeyFormat;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyMintHardwareInfo;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.VerificationToken;
/**
* KeyMint device definition.
diff --git a/keymint/aidl/android/hardware/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
similarity index 97%
rename from keymint/aidl/android/hardware/keymint/IKeyMintOperation.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index 1b79296..24960cc 100644
--- a/keymint/aidl/android/hardware/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.ByteArray;
-import android.hardware.keymint.HardwareAuthToken;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.KeyParameterArray;
-import android.hardware.keymint.VerificationToken;
+import android.hardware.security.keymint.ByteArray;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameterArray;
+import android.hardware.security.keymint.VerificationToken;
@VintfStability
interface IKeyMintOperation {
diff --git a/keymint/aidl/android/hardware/keymint/KeyCharacteristics.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
similarity index 94%
rename from keymint/aidl/android/hardware/keymint/KeyCharacteristics.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
index ac7c2b4..0801868 100644
--- a/keymint/aidl/android/hardware/keymint/KeyCharacteristics.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
/**
* KeyCharacteristics defines the attributes of a key, including cryptographic parameters, and usage
diff --git a/keymint/aidl/android/hardware/keymint/KeyFormat.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
similarity index 95%
rename from keymint/aidl/android/hardware/keymint/KeyFormat.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
index 13044dc..6ad8e3d 100644
--- a/keymint/aidl/android/hardware/keymint/KeyFormat.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.security.keymint;
/**
* Formats for key import and export.
diff --git a/keymint/aidl/android/hardware/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
similarity index 94%
rename from keymint/aidl/android/hardware/keymint/KeyMintHardwareInfo.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
index 5815b10..d3d7368a 100644
--- a/keymint/aidl/android/hardware/keymint/KeyMintHardwareInfo.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.SecurityLevel;
-
+import android.hardware.security.keymint.SecurityLevel;
/**
* KeyMintHardwareInfo is the hardware information returned by calling KeyMint getHardwareInfo()
*/
-
@VintfStability
parcelable KeyMintHardwareInfo {
/**
diff --git a/keymint/aidl/android/hardware/keymint/KeyOrigin.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
similarity index 96%
rename from keymint/aidl/android/hardware/keymint/KeyOrigin.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
index 70320d3..0cd53c2 100644
--- a/keymint/aidl/android/hardware/keymint/KeyOrigin.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
-
+package android.hardware.security.keymint;
/**
* The origin of a key (or pair), i.e. where it was generated. Note that ORIGIN can be found in
diff --git a/keymint/aidl/android/hardware/keymint/KeyParameterArray.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
similarity index 72%
copy from keymint/aidl/android/hardware/keymint/KeyParameterArray.aidl
copy to security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
index cc9e37a..f3ed96b 100644
--- a/keymint/aidl/android/hardware/keymint/KeyParameterArray.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.Tag;
+import android.hardware.security.keymint.KeyParameterValue;
/**
* Identifies the key authorization parameters to be used with keyMint. This is usually
* provided as an array of KeyParameters to IKeyMintDevice or Operation.
*/
@VintfStability
-parcelable KeyParameterArray {
- /**
- * Identify list of key parameters corresponding to a particular key blob.
- */
- KeyParameter[] params;
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyParameter {
+ Tag tag;
+ KeyParameterValue value;
}
diff --git a/keymint/aidl/android/hardware/keymint/KeyParameterArray.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
similarity index 90%
rename from keymint/aidl/android/hardware/keymint/KeyParameterArray.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
index cc9e37a..acab435 100644
--- a/keymint/aidl/android/hardware/keymint/KeyParameterArray.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
/**
* Identifies the key authorization parameters to be used with keyMint. This is usually
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
new file mode 100644
index 0000000..a4f5154
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.security.keymint;
+
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.BlockMode;
+import android.hardware.security.keymint.Digest;
+import android.hardware.security.keymint.EcCurve;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+import android.hardware.security.keymint.KeyOrigin;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.PaddingMode;
+import android.hardware.security.keymint.SecurityLevel;
+
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+union KeyParameterValue {
+
+ /* Represents an invalid value type. */
+ int invalid;
+
+ /* Enum types */
+ Algorithm algorithm;
+ BlockMode blockMode;
+ PaddingMode paddingMode;
+ Digest digest;
+ EcCurve ecCurve;
+ KeyOrigin origin;
+ KeyPurpose keyPurpose;
+ HardwareAuthenticatorType hardwareAuthenticatorType;
+ SecurityLevel securityLevel;
+
+ /* Other types */
+ boolean boolValue; // Always true, if present.
+ int integer;
+ long longInteger;
+ long dateTime;
+
+ byte[] blob;
+}
diff --git a/keymint/aidl/android/hardware/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
similarity index 95%
rename from keymint/aidl/android/hardware/keymint/KeyPurpose.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
index bc029fd..cb4682e 100644
--- a/keymint/aidl/android/hardware/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
/**
diff --git a/keymint/aidl/android/hardware/keymint/PaddingMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
similarity index 96%
rename from keymint/aidl/android/hardware/keymint/PaddingMode.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
index 337ed91..80b73bd 100644
--- a/keymint/aidl/android/hardware/keymint/PaddingMode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
/**
* TODO(seleneh) update the description.
diff --git a/keymint/aidl/android/hardware/keymint/SecurityLevel.aidl b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
similarity index 95%
rename from keymint/aidl/android/hardware/keymint/SecurityLevel.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
index d8de024..10363e9 100644
--- a/keymint/aidl/android/hardware/keymint/SecurityLevel.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
/**
* Device security levels.
diff --git a/keymint/aidl/android/hardware/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
similarity index 89%
rename from keymint/aidl/android/hardware/keymint/Tag.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index 46da096..9f41b4e 100644
--- a/keymint/aidl/android/hardware/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.TagType;
+import android.hardware.security.keymint.TagType;
// TODO(seleneh) : note aidl currently does not support double nested enum definitions such as
// ROOT_OF_TRUST = TagType:BYTES | 704. So we are forced to write definations as
@@ -46,7 +46,7 @@
*
* Must be hardware-enforced.
*/
- PURPOSE = (2 << 28) | 1, /* TagType:ENUM_REP */
+ PURPOSE = (2 << 28) /* TagType:ENUM_REP */ | 1,
/**
* Tag::ALGORITHM specifies the cryptographic algorithm with which the key is used. This tag
@@ -55,7 +55,7 @@
*
* Must be hardware-enforced.
*/
- ALGORITHM = (1 << 28) | 2, /* TagType:ENUM */
+ ALGORITHM = (1 << 28) /* TagType:ENUM */ | 2,
/**
* Tag::KEY_SIZE pecifies the size, in bits, of the key, measuring in the normal way for the
@@ -67,7 +67,7 @@
*
* Must be hardware-enforced.
*/
- KEY_SIZE = (3 << 28) | 3, /* TagType:UINT */
+ KEY_SIZE = (3 << 28) /* TagType:UINT */ | 3,
/**
* Tag::BLOCK_MODE specifies the block cipher mode(s) with which the key may be used. This tag
@@ -80,8 +80,8 @@
*
* Must be hardware-enforced.
*/
- BLOCK_MODE = (2 << 28) | 4,
- /* BlockMode. */ /* TagType:ENUM_REP */
+ BLOCK_MODE = (2 << 28) /* TagType:ENUM_REP */ | 4,
+
/**
* Tag::DIGEST specifies the digest algorithms that may be used with the key to perform signing
@@ -95,7 +95,7 @@
*
* Must be hardware-enforced.
*/
- DIGEST = (2 << 28) | 5, /* TagType:ENUM_REP */
+ DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 5,
/**
* Tag::PADDING specifies the padding modes that may be used with the key. This tag is relevant
@@ -123,7 +123,7 @@
*
* Must be hardware-enforced.
*/
- PADDING = (2 << 28) | 6, /* TagType:ENUM_REP */
+ PADDING = (2 << 28) /* TagType:ENUM_REP */ | 6,
/**
* Tag::CALLER_NONCE specifies that the caller can provide a nonce for nonce-requiring
@@ -136,7 +136,7 @@
*
* Must be hardware-enforced.
*/
- CALLER_NONCE = (7 << 28) | 7, /* TagType:BOOL */
+ CALLER_NONCE = (7 << 28) /* TagType:BOOL */ | 7,
/**
* Tag::MIN_MAC_LENGTH specifies the minimum length of MAC that can be requested or verified
@@ -149,7 +149,7 @@
*
* Must be hardware-enforced.
*/
- MIN_MAC_LENGTH = (3 << 28) | 8, /* TagType:UINT */
+ MIN_MAC_LENGTH = (3 << 28) /* TagType:UINT */ | 8,
// Tag 9 reserved
@@ -160,7 +160,7 @@
*
* Must be hardware-enforced.
*/
- EC_CURVE = (1 << 28) | 10, /* TagType:ENUM */
+ EC_CURVE = (1 << 28) /* TagType:ENUM */ | 10,
/**
* Tag::RSA_PUBLIC_EXPONENT specifies the value of the public exponent for an RSA key pair.
@@ -174,7 +174,7 @@
*
* Must be hardware-enforced.
*/
- RSA_PUBLIC_EXPONENT = (5 << 28) | 200, /* TagType:ULONG */
+ RSA_PUBLIC_EXPONENT = (5 << 28) /* TagType:ULONG */ | 200,
// Tag 201 reserved
@@ -185,7 +185,23 @@
*
* Must be hardware-enforced.
*/
- INCLUDE_UNIQUE_ID = (7 << 28) | 202, /* TagType:BOOL */
+ INCLUDE_UNIQUE_ID = (7 << 28) /* TagType:BOOL */ | 202,
+
+ /**
+ * Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with
+ * RSA encryption/decryption with OAEP padding. If the key characteristics supports OAEP
+ * and this tag is absent then SHA1 digest is selected by default for MGF1.
+ *
+ * This tag is repeatable for key generation/import. If this tag is present in the key
+ * characteristics with one or more values from @4.0::Digest, then for RSA cipher
+ * operations with OAEP Padding, the caller must specify a digest in the additionalParams
+ * argument of begin operation. If this tag is missing or the specified digest is not in
+ * the digests associated with the key then begin operation must fail with
+ * ErrorCode::INCOMPATIBLE_MGF_DIGEST.
+ *
+ * Must be hardware-enforced.
+ */
+ RSA_OAEP_MGF_DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 203,
/**
* TODO(seleneh) this tag needs to be deleted from all codes.
@@ -202,7 +218,7 @@
*
* Must be hardware-enforced.
*/
- BLOB_USAGE_REQUIREMENTS = (1 << 28) | 301, /* TagType:ENUM */
+ BLOB_USAGE_REQUIREMENTS = (1 << 28) /* TagType:ENUM */ | 301,
/**
* Tag::BOOTLOADER_ONLY specifies only the bootloader can use the key.
@@ -212,7 +228,7 @@
*
* Must be hardware-enforced.
*/
- BOOTLOADER_ONLY = (7 << 28) | 302, /* TagType:BOOL */
+ BOOTLOADER_ONLY = (7 << 28) /* TagType:BOOL */ | 302,
/**
* Tag::ROLLBACK_RESISTANCE specifies that the key has rollback resistance, meaning that when
@@ -227,16 +243,16 @@
*
* Must be hardwared-enforced.
*/
- ROLLBACK_RESISTANCE = (7 << 28) | 303, /* TagType:BOOL */
+ ROLLBACK_RESISTANCE = (7 << 28) /* TagType:BOOL */ | 303,
// Reserved for future use.
- HARDWARE_TYPE = (1 << 28) | 304, /* TagType:ENUM */
+ HARDWARE_TYPE = (1 << 28) /* TagType:ENUM */ | 304,
/**
* Keys tagged with EARLY_BOOT_ONLY may only be used, or created, during early boot, until
* IKeyMintDevice::earlyBootEnded() is called.
*/
- EARLY_BOOT_ONLY = (7 << 28) | 305, /* TagType:BOOL */
+ EARLY_BOOT_ONLY = (7 << 28) /* TagType:BOOL */ | 305,
/**
* Tag::ACTIVE_DATETIME specifies the date and time at which the key becomes active, in
@@ -245,8 +261,7 @@
*
* Need not be hardware-enforced.
*/
- ACTIVE_DATETIME = (6 << 28) | 400,
- /* Start of validity. */ /* TagType:DATE */
+ ACTIVE_DATETIME = (6 << 28) /* TagType:DATE */ | 400,
/**
* Tag::ORIGINATION_EXPIRE_DATETIME specifies the date and time at which the key expires for
@@ -258,7 +273,7 @@
*
* Need not be hardware-enforced.
*/
- ORIGINATION_EXPIRE_DATETIME = (6 << 28) | 401, /* TagType:DATE */
+ ORIGINATION_EXPIRE_DATETIME = (6 << 28) /* TagType:DATE */ | 401,
/**
* Tag::USAGE_EXPIRE_DATETIME specifies the date and time at which the key expires for
@@ -270,7 +285,7 @@
*
* Need not be hardware-enforced.
*/
- USAGE_EXPIRE_DATETIME = (6 << 28) | 402, /* TagType:DATE */
+ USAGE_EXPIRE_DATETIME = (6 << 28) /* TagType:DATE */ | 402,
/**
* TODO(seleneh) this tag need to be deleted.
@@ -295,7 +310,7 @@
*
* Must be hardware-enforced.
*/
- MIN_SECONDS_BETWEEN_OPS = (3 << 28) | 403, /* TagType:UINT */
+ MIN_SECONDS_BETWEEN_OPS = (3 << 28) /* TagType:UINT */ | 403,
/**
* Tag::MAX_USES_PER_BOOT specifies the maximum number of times that a key may be used between
@@ -315,14 +330,14 @@
*
* Must be hardware-enforced.
*/
- MAX_USES_PER_BOOT = (3 << 28) | 404, /* TagType:UINT */
+ MAX_USES_PER_BOOT = (3 << 28) /* TagType:UINT */ | 404,
/**
* Tag::USER_ID specifies the ID of the Android user that is permitted to use the key.
*
* Must not be hardware-enforced.
*/
- USER_ID = (3 << 28) | 501, /* TagType:UINT */
+ USER_ID = (3 << 28) /* TagType:UINT */ | 501,
/**
* Tag::USER_SECURE_ID specifies that a key may only be used under a particular secure user
@@ -355,7 +370,7 @@
*
* Must be hardware-enforced.
*/
- USER_SECURE_ID = (10 << 28) | 502, /* TagType:ULONG_REP */
+ USER_SECURE_ID = (10 << 28) /* TagType:ULONG_REP */ | 502,
/**
* Tag::NO_AUTH_REQUIRED specifies that no authentication is required to use this key. This tag
@@ -363,7 +378,7 @@
*
* Must be hardware-enforced.
*/
- NO_AUTH_REQUIRED = (7 << 28) | 503, /* TagType:BOOL */
+ NO_AUTH_REQUIRED = (7 << 28) /* TagType:BOOL */ | 503,
/**
* Tag::USER_AUTH_TYPE specifies the types of user authenticators that may be used to authorize
@@ -382,7 +397,7 @@
*
* Must be hardware-enforced.
*/
- USER_AUTH_TYPE = (1 << 28) | 504, /* TagType:ENUM */
+ USER_AUTH_TYPE = (1 << 28) /* TagType:ENUM */ | 504,
/**
* Tag::AUTH_TIMEOUT specifies the time in seconds for which the key is authorized for use,
@@ -396,7 +411,7 @@
*
* Must be hardware-enforced.
*/
- AUTH_TIMEOUT = (3 << 28) | 505, /* TagType:UINT */
+ AUTH_TIMEOUT = (3 << 28) /* TagType:UINT */ | 505,
/**
* Tag::ALLOW_WHILE_ON_BODY specifies that the key may be used after authentication timeout if
@@ -404,7 +419,7 @@
*
* Cannot be hardware-enforced.
*/
- ALLOW_WHILE_ON_BODY = (7 << 28) | 506, /* TagType:BOOL */
+ ALLOW_WHILE_ON_BODY = (7 << 28) /* TagType:BOOL */ | 506,
/**
* TRUSTED_USER_PRESENCE_REQUIRED is an optional feature that specifies that this key must be
@@ -451,7 +466,7 @@
*
* Must be hardware-enforced.
*/
- TRUSTED_USER_PRESENCE_REQUIRED = (7 << 28) | 507, /* TagType:BOOL */
+ TRUSTED_USER_PRESENCE_REQUIRED = (7 << 28) /* TagType:BOOL */ | 507,
/** Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
* specifies that this key must not be usable unless the user provides confirmation of the data
@@ -464,7 +479,7 @@
*
* Must be hardware-enforced.
*/
- TRUSTED_CONFIRMATION_REQUIRED = (7 << 28) | 508, /* TagType:BOOL */
+ TRUSTED_CONFIRMATION_REQUIRED = (7 << 28) /* TagType:BOOL */ | 508,
/**
* Tag::UNLOCKED_DEVICE_REQUIRED specifies that the key may only be used when the device is
@@ -472,7 +487,7 @@
*
* Must be software-enforced.
*/
- UNLOCKED_DEVICE_REQUIRED = (7 << 28) | 509, /* TagType:BOOL */
+ UNLOCKED_DEVICE_REQUIRED = (7 << 28) /* TagType:BOOL */ | 509,
/**
* Tag::APPLICATION_ID. When provided to generateKey or importKey, this tag specifies data
@@ -488,7 +503,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- APPLICATION_ID = (9 << 28) | 601, /* TagType:BYTES */
+ APPLICATION_ID = (9 << 28) /* TagType:BYTES */ | 601,
/*
* Semantically unenforceable tags, either because they have no specific meaning or because
@@ -509,7 +524,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- APPLICATION_DATA = (9 << 28) | 700, /* TagType:BYTES */
+ APPLICATION_DATA = (9 << 28) /* TagType:BYTES */ | 700,
/**
* Tag::CREATION_DATETIME specifies the date and time the key was created, in milliseconds since
@@ -518,7 +533,7 @@
* Tag::CREATED is informational only, and not enforced by anything. Must be in the
* software-enforced list, if provided.
*/
- CREATION_DATETIME = (6 << 28) | 701, /* TagType:DATE */
+ CREATION_DATETIME = (6 << 28) /* TagType:DATE */ | 701,
/**
* Tag::ORIGIN specifies where the key was created, if known. This tag must not be specified
@@ -527,7 +542,7 @@
*
* Must be hardware-enforced.
*/
- ORIGIN = (1 << 28) | 702, /* TagType:ENUM */
+ ORIGIN = (1 << 28) /* TagType:ENUM */ | 702,
// 703 is unused.
@@ -539,7 +554,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ROOT_OF_TRUST = (9 << 28) | 704, /* TagType:BYTES */
+ ROOT_OF_TRUST = (9 << 28) /* TagType:BYTES */ | 704,
/**
* Tag::OS_VERSION specifies the system OS version with which the key may be used. This tag is
@@ -562,7 +577,7 @@
*
* Must be hardware-enforced.
*/
- OS_VERSION = (3 << 28) | 705, /* TagType:UINT */
+ OS_VERSION = (3 << 28) /* TagType:UINT */ | 705,
/**
* Tag::OS_PATCHLEVEL specifies the system security patch level with which the key may be used.
@@ -583,7 +598,7 @@
*
* Must be hardware-enforced.
*/
- OS_PATCHLEVEL = (3 << 28) | 706, /* TagType:UINT */
+ OS_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 706,
/**
* Tag::UNIQUE_ID specifies a unique, time-based identifier. This tag is never provided to or
@@ -617,7 +632,7 @@
*
* Must be hardware-enforced.
*/
- UNIQUE_ID = (9 << 28) | 707, /* TagType:BYTES */
+ UNIQUE_ID = (9 << 28) /* TagType:BYTES */ | 707,
/**
* Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attestKey() method,
@@ -626,7 +641,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_CHALLENGE = (9 << 28) | 708, /* TagType:BYTES */
+ ATTESTATION_CHALLENGE = (9 << 28) /* TagType:BYTES */ | 708,
/**
* Tag::ATTESTATION_APPLICATION_ID identifies the set of applications which may use a key, used
@@ -652,7 +667,7 @@
*
* Cannot be hardware-enforced.
*/
- ATTESTATION_APPLICATION_ID = (9 << 28) | 709, /* TagType:BYTES */
+ ATTESTATION_APPLICATION_ID = (9 << 28) /* TagType:BYTES */ | 709,
/**
* Tag::ATTESTATION_ID_BRAND provides the device's brand name, as returned by Build.BRAND in
@@ -665,7 +680,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_ID_BRAND = (9 << 28) | 710, /* TagType:BYTES */
+ ATTESTATION_ID_BRAND = (9 << 28) /* TagType:BYTES */ | 710,
/**
* Tag::ATTESTATION_ID_DEVICE provides the device's device name, as returned by Build.DEVICE in
@@ -678,7 +693,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_ID_DEVICE = (9 << 28) | 711, /* TagType:BYTES */
+ ATTESTATION_ID_DEVICE = (9 << 28) /* TagType:BYTES */ | 711,
/**
* Tag::ATTESTATION_ID_PRODUCT provides the device's product name, as returned by Build.PRODUCT
@@ -691,7 +706,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_ID_PRODUCT = (9 << 28) | 712, /* TagType:BYTES */
+ ATTESTATION_ID_PRODUCT = (9 << 28) /* TagType:BYTES */ | 712,
/**
* Tag::ATTESTATION_ID_SERIAL the device's serial number. This field must be set only when
@@ -703,7 +718,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_ID_SERIAL = (9 << 28) | 713, /* TagType:BYTES */
+ ATTESTATION_ID_SERIAL = (9 << 28) /* TagType:BYTES */ | 713,
/**
* Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attestKey().
@@ -715,7 +730,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_ID_IMEI = (9 << 28) | 714, /* TagType:BYTES */
+ ATTESTATION_ID_IMEI = (9 << 28) /* TagType:BYTES */ | 714,
/**
* Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attestKey().
@@ -727,7 +742,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_ID_MEID = (9 << 28) | 715, /* TagType:BYTES */
+ ATTESTATION_ID_MEID = (9 << 28) /* TagType:BYTES */ | 715,
/**
* Tag::ATTESTATION_ID_MANUFACTURER provides the device's manufacturer name, as returned by
@@ -740,7 +755,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_ID_MANUFACTURER = (9 << 28) | 716, /* TagType:BYTES */
+ ATTESTATION_ID_MANUFACTURER = (9 << 28) /* TagType:BYTES */ | 716,
/**
* Tag::ATTESTATION_ID_MODEL provides the device's model name, as returned by Build.MODEL in
@@ -753,7 +768,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- ATTESTATION_ID_MODEL = (9 << 28) | 717, /* TagType:BYTES */
+ ATTESTATION_ID_MODEL = (9 << 28) /* TagType:BYTES */ | 717,
/**
* Tag::VENDOR_PATCHLEVEL specifies the vendor image security patch level with which the key may
@@ -775,7 +790,7 @@
*
* Must be hardware-enforced.
*/
- VENDOR_PATCHLEVEL = (3 << 28) | 718, /* TagType:UINT */
+ VENDOR_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 718,
/**
* Tag::BOOT_PATCHLEVEL specifies the boot image (kernel) security patch level with which the
@@ -795,7 +810,7 @@
*
* Must be hardware-enforced.
*/
- BOOT_PATCHLEVEL = (3 << 28) | 719, /* TagType:UINT */
+ BOOT_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 719,
/**
* DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attestKey(). It indicates that
@@ -811,7 +826,7 @@
* IKeyMintDevice implementations that support device-unique attestation MUST add the
* DEVICE_UNIQUE_ATTESTATION tag to device-unique attestations.
*/
- DEVICE_UNIQUE_ATTESTATION = (7 << 28) | 720, /* TagType:BOOL */
+ DEVICE_UNIQUE_ATTESTATION = (7 << 28) /* TagType:BOOL */ | 720,
/**
* IDENTITY_CREDENTIAL_KEY is never used by IKeyMintDevice, is not a valid argument to key
@@ -819,7 +834,7 @@
* attestation. It is used in attestations produced by the IIdentityCredential HAL when that
* HAL attests to Credential Keys. IIdentityCredential produces KeyMint-style attestations.
*/
- IDENTITY_CREDENTIAL_KEY = (7 << 28) | 721, /* TagType:BOOL */
+ IDENTITY_CREDENTIAL_KEY = (7 << 28) /* TagType:BOOL */ | 721,
/**
* To prevent keys from being compromised if an attacker acquires read access to system / kernel
@@ -836,7 +851,7 @@
* ErrorCode::INVALID_OPERATION is returned when a key with Tag::STORAGE_KEY is provided to
* begin().
*/
- STORAGE_KEY = (7 << 28) | 722, /* TagType:BOOL */
+ STORAGE_KEY = (7 << 28) /* TagType:BOOL */ | 722,
/**
* Tag::ASSOCIATED_DATA Provides "associated data" for AES-GCM encryption or decryption. This
@@ -845,7 +860,7 @@
*
* Must never appear KeyCharacteristics.
*/
- ASSOCIATED_DATA = (9 << 28) | 1000, /* TagType:BYTES */
+ ASSOCIATED_DATA = (9 << 28) /* TagType:BYTES */ | 1000,
/**
* Tag::NONCE is used to provide or return a nonce or Initialization Vector (IV) for AES-GCM,
@@ -860,7 +875,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- NONCE = (9 << 28) | 1001, /* TagType:BYTES */
+ NONCE = (9 << 28) /* TagType:BYTES */ | 1001,
/**
* Tag::MAC_LENGTH provides the requested length of a MAC or GCM authentication tag, in bits.
@@ -871,7 +886,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- MAC_LENGTH = (3 << 28) | 1003, /* TagType:UINT */
+ MAC_LENGTH = (3 << 28) /* TagType:UINT */ | 1003,
/**
* Tag::RESET_SINCE_ID_ROTATION specifies whether the device has been factory reset since the
@@ -879,7 +894,7 @@
*
* Must never appear in KeyCharacteristics.
*/
- RESET_SINCE_ID_ROTATION = (7 << 28) | 1004, /* TagType:BOOL */
+ RESET_SINCE_ID_ROTATION = (7 << 28) /* TagType:BOOL */ | 1004,
/**
* Tag::CONFIRMATION_TOKEN is used to deliver a cryptographic token proving that the user
@@ -888,5 +903,5 @@
*
* Must never appear in KeyCharacteristics.
*/
- CONFIRMATION_TOKEN = (9 << 28) | 1005, /* TagType:BYTES */
+ CONFIRMATION_TOKEN = (9 << 28) /* TagType:BYTES */ | 1005,
}
diff --git a/keymint/aidl/android/hardware/keymint/TagType.aidl b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
similarity index 96%
rename from keymint/aidl/android/hardware/keymint/TagType.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
index fb50b10..a273af3 100644
--- a/keymint/aidl/android/hardware/keymint/TagType.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
/**
* TagType classifies Tags in Tag.aidl into various groups of data.
diff --git a/keymint/aidl/android/hardware/keymint/Timestamp.aidl b/security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl
similarity index 95%
rename from keymint/aidl/android/hardware/keymint/Timestamp.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl
index 7c882c6..ebb3684 100644
--- a/keymint/aidl/android/hardware/keymint/Timestamp.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
/**
* Time in milliseconds since some arbitrary point in time. Time must be monotonically increasing,
diff --git a/keymint/aidl/android/hardware/keymint/VerificationToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl
similarity index 79%
rename from keymint/aidl/android/hardware/keymint/VerificationToken.aidl
rename to security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl
index 736c0e2..f76e6a8 100644
--- a/keymint/aidl/android/hardware/keymint/VerificationToken.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.hardware.keymint;
+package android.hardware.security.keymint;
-import android.hardware.keymint.SecurityLevel;
-import android.hardware.keymint.Timestamp;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Timestamp;
/**
* VerificationToken instances are used for secure environments to authenticate one another.
@@ -48,7 +48,7 @@
* 32-byte HMAC-SHA256 of the above values, computed as:
*
* HMAC(H,
- * "Auth Verification" || challenge || timestamp || securityLevel || parametersVerified)
+ * "Auth Verification" || challenge || timestamp || securityLevel)
*
* where:
*
@@ -58,11 +58,6 @@
*
* The representation of challenge and timestamp is as 64-bit unsigned integers in big-endian
* order. securityLevel is represented as a 32-bit unsigned integer in big-endian order.
- *
- * If parametersVerified is non-empty, the representation of parametersVerified is an ASN.1 DER
- * encoded representation of the values. The ASN.1 schema used is the AuthorizationList schema
- * from the Keystore attestation documentation. If parametersVerified is empty, it is simply
- * omitted from the HMAC computation.
*/
byte[] mac;
}
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
new file mode 100644
index 0000000..79697c4
--- /dev/null
+++ b/security/keymint/aidl/default/Android.bp
@@ -0,0 +1,26 @@
+cc_binary {
+ name: "android.hardware.security.keymint-service",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.security.keymint-service.rc"],
+ vintf_fragments: ["android.hardware.security.keymint-service.xml"],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "android.hardware.security.keymint-unstable-ndk_platform",
+ "libbase",
+ "libbinder_ndk",
+ "libcppbor",
+ "libcrypto",
+ "libkeymaster_portable",
+ "libkeymint",
+ "liblog",
+ "libpuresoftkeymasterdevice",
+ "libutils",
+ ],
+ srcs: [
+ "service.cpp",
+ ],
+}
diff --git a/security/keymint/aidl/default/android.hardware.security.keymint-service.rc b/security/keymint/aidl/default/android.hardware.security.keymint-service.rc
new file mode 100644
index 0000000..0c3a6e1
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.rc
@@ -0,0 +1,3 @@
+service vendor.keymint-default /vendor/bin/hw/android.hardware.security.keymint-service
+ class early_hal
+ user nobody
diff --git a/keymint/aidl/default/android.hardware.keymint@1.0-service.xml b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
similarity index 70%
rename from keymint/aidl/default/android.hardware.keymint@1.0-service.xml
rename to security/keymint/aidl/default/android.hardware.security.keymint-service.xml
index 3935b5a..73d15a8 100644
--- a/keymint/aidl/default/android.hardware.keymint@1.0-service.xml
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
@@ -1,6 +1,6 @@
<manifest version="1.0" type="device">
<hal format="aidl">
- <name>android.hardware.keymint</name>
+ <name>android.hardware.security.keymint</name>
<fqname>IKeyMintDevice/default</fqname>
</hal>
</manifest>
diff --git a/keymint/aidl/default/service.cpp b/security/keymint/aidl/default/service.cpp
similarity index 68%
rename from keymint/aidl/default/service.cpp
rename to security/keymint/aidl/default/service.cpp
index ca5555e..a710535 100644
--- a/keymint/aidl/default/service.cpp
+++ b/security/keymint/aidl/default/service.cpp
@@ -14,30 +14,30 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.keymint1-service"
+#define LOG_TAG "android.hardware.security.keymint-service"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
-#include <AndroidKeyMint1Device.h>
+#include <AndroidKeyMintDevice.h>
#include <keymaster/soft_keymaster_logger.h>
-using aidl::android::hardware::keymint::SecurityLevel;
-using aidl::android::hardware::keymint::V1_0::AndroidKeyMint1Device;
+using aidl::android::hardware::security::keymint::AndroidKeyMintDevice;
+using aidl::android::hardware::security::keymint::SecurityLevel;
int main() {
// Zero threads seems like a useless pool, but below we'll join this thread to it, increasing
// the pool size to 1.
ABinderProcess_setThreadPoolMaxThreadCount(0);
- std::shared_ptr<AndroidKeyMint1Device> km5 =
- ndk::SharedRefBase::make<AndroidKeyMint1Device>(SecurityLevel::SOFTWARE);
+ std::shared_ptr<AndroidKeyMintDevice> keyMint =
+ ndk::SharedRefBase::make<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
keymaster::SoftKeymasterLogger logger;
- const auto instanceName = std::string(AndroidKeyMint1Device::descriptor) + "/default";
+ const auto instanceName = std::string(AndroidKeyMintDevice::descriptor) + "/default";
LOG(INFO) << "instance: " << instanceName;
binder_status_t status =
- AServiceManager_addService(km5->asBinder().get(), instanceName.c_str());
+ AServiceManager_addService(keyMint->asBinder().get(), instanceName.c_str());
CHECK(status == STATUS_OK);
ABinderProcess_joinThreadPool();
diff --git a/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
similarity index 73%
rename from keymint/aidl/vts/functional/Android.bp
rename to security/keymint/aidl/vts/functional/Android.bp
index 9ee8239..c7cc380 100644
--- a/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -15,25 +15,25 @@
//
cc_test {
- name: "VtsAidlKeyMintV1_0TargetTest",
+ name: "VtsAidlKeyMintTargetTest",
defaults: [
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
srcs: [
- "keyMint1Test.cpp",
+ "KeyMintTest.cpp",
"VerificationTokenTest.cpp",
],
shared_libs: [
- "libbinder",
+ "libbinder_ndk",
"libcrypto",
- "libkeymint1",
- "libkeymintSupport",
+ "libkeymint",
+ "libkeymint_support",
],
static_libs: [
- "android.hardware.keymint-cpp",
- "libcppbor",
- "libkeyMint1VtsTestUtil",
+ "android.hardware.security.keymint-unstable-ndk_platform",
+ "libcppbor_external",
+ "libkeymint_vts_test_utils",
],
test_suites: [
"general-tests",
@@ -42,7 +42,7 @@
}
cc_test_library {
- name: "libkeyMint1VtsTestUtil",
+ name: "libkeymint_vts_test_utils",
defaults: [
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
@@ -54,13 +54,13 @@
".",
],
shared_libs: [
- "libbinder",
+ "libbinder_ndk",
"libcrypto",
- "libkeymint1",
- "libkeymintSupport",
+ "libkeymint",
+ "libkeymint_support",
],
static_libs: [
- "android.hardware.keymint-cpp",
+ "android.hardware.security.keymint-unstable-ndk_platform",
"libcppbor",
],
}
diff --git a/keymint/aidl/vts/functional/AndroidTest.xml b/security/keymint/aidl/vts/functional/AndroidTest.xml
similarity index 82%
rename from keymint/aidl/vts/functional/AndroidTest.xml
rename to security/keymint/aidl/vts/functional/AndroidTest.xml
index 43e7a8a..de543f1 100644
--- a/keymint/aidl/vts/functional/AndroidTest.xml
+++ b/security/keymint/aidl/vts/functional/AndroidTest.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Runs VtsAidlKeyMintV1_0TargetTest.">
+<configuration description="Runs VtsAidlKeyMintTargetTest.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
@@ -22,12 +22,13 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="VtsAidlKeyMintV1_0TargetTest->/data/local/tmp/VtsAidlKeyMintV1_0TargetTest" />
+ <option name="push"
+ value="VtsAidlKeyMintTargetTest->/data/local/tmp/VtsAidlKeyMintTargetTest" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="VtsAidlKeyMintV1_0TargetTest" />
+ <option name="module-name" value="VtsAidlKeyMintTargetTest" />
<option name="native-test-timeout" value="900000"/>
</test>
</configuration>
diff --git a/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
similarity index 94%
rename from keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
rename to security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 0546149..94bc199 100644
--- a/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -20,13 +20,12 @@
#include <vector>
#include <android-base/logging.h>
+#include <android/binder_manager.h>
-#include <keymintSupport/key_param_output.h>
-#include <keymintSupport/keymint_utils.h>
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/keymint_utils.h>
-namespace android {
-namespace hardware {
-namespace keymint {
+namespace aidl::android::hardware::security::keymint {
using namespace std::literals::chrono_literals;
using std::endl;
@@ -44,19 +43,19 @@
namespace test {
-ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(Status result) {
+ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(const Status& result) {
if (result.isOk()) return ErrorCode::OK;
- if (result.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
- return static_cast<ErrorCode>(result.serviceSpecificErrorCode());
+ if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ return static_cast<ErrorCode>(result.getServiceSpecificError());
}
return ErrorCode::UNKNOWN_ERROR;
}
-void KeyMintAidlTestBase::InitializeKeyMint(sp<IKeyMintDevice> keyMint) {
+void KeyMintAidlTestBase::InitializeKeyMint(std::shared_ptr<IKeyMintDevice> keyMint) {
ASSERT_NE(keyMint, nullptr);
- keymint_ = keyMint;
+ keymint_ = std::move(keyMint);
KeyMintHardwareInfo info;
ASSERT_TRUE(keymint_->getHardwareInfo(&info).isOk());
@@ -70,8 +69,12 @@
}
void KeyMintAidlTestBase::SetUp() {
- InitializeKeyMint(
- android::waitForDeclaredService<IKeyMintDevice>(String16(GetParam().c_str())));
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ InitializeKeyMint(IKeyMintDevice::fromBinder(binder));
+ } else {
+ InitializeKeyMint(nullptr);
+ }
}
ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
@@ -178,7 +181,7 @@
*key_blob = vector<uint8_t>();
}
- EXPECT_TRUE(result.isOk()) << result.serviceSpecificErrorCode() << endl;
+ EXPECT_TRUE(result.isOk()) << result.getServiceSpecificError() << endl;
return GetReturnErrorCode(result);
}
@@ -188,7 +191,7 @@
ErrorCode KeyMintAidlTestBase::DeleteAllKeys() {
Status result = keymint_->deleteAllKeys();
- EXPECT_TRUE(result.isOk()) << result.serviceSpecificErrorCode() << endl;
+ EXPECT_TRUE(result.isOk()) << result.getServiceSpecificError() << endl;
return GetReturnErrorCode(result);
}
@@ -203,7 +206,8 @@
ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
const AuthorizationSet& in_params,
- AuthorizationSet* out_params, sp<IKeyMintOperation>& op) {
+ AuthorizationSet* out_params,
+ std::shared_ptr<IKeyMintOperation>& op) {
SCOPED_TRACE("Begin");
Status result;
BeginResult out;
@@ -328,7 +332,7 @@
output->append(oPut.begin(), oPut.end());
}
- op_.clear(); // So dtor doesn't Abort().
+ op_.reset();
return GetReturnErrorCode(result);
}
@@ -360,7 +364,7 @@
return result;
}
-ErrorCode KeyMintAidlTestBase::Abort(const sp<IKeyMintOperation>& op) {
+ErrorCode KeyMintAidlTestBase::Abort(const std::shared_ptr<IKeyMintOperation>& op) {
SCOPED_TRACE("Abort");
EXPECT_NE(op, nullptr);
@@ -370,7 +374,7 @@
Status retval = op->abort();
EXPECT_TRUE(retval.isOk());
- return static_cast<ErrorCode>(retval.serviceSpecificErrorCode());
+ return static_cast<ErrorCode>(retval.getServiceSpecificError());
}
ErrorCode KeyMintAidlTestBase::Abort() {
@@ -382,14 +386,14 @@
}
Status retval = op_->abort();
- return static_cast<ErrorCode>(retval.serviceSpecificErrorCode());
+ return static_cast<ErrorCode>(retval.getServiceSpecificError());
}
void KeyMintAidlTestBase::AbortIfNeeded() {
SCOPED_TRACE("AbortIfNeeded");
if (op_) {
EXPECT_EQ(ErrorCode::OK, Abort());
- op_.clear();
+ op_.reset();
}
}
@@ -524,7 +528,7 @@
AuthorizationSet finish_out_params;
EXPECT_EQ(ErrorCode::OK, Finish(finish_params, message.substr(consumed), signature,
&finish_out_params, &output));
- op_.clear();
+ op_.reset();
EXPECT_TRUE(output.empty());
}
@@ -573,8 +577,8 @@
string ciphertext = EncryptMessage(message, params, &out_params);
EXPECT_EQ(1U, out_params.size());
auto ivVal = out_params.GetTagValue(TAG_NONCE);
- EXPECT_TRUE(ivVal.isOk());
- if (ivVal.isOk()) *iv_out = ivVal.value();
+ EXPECT_TRUE(ivVal);
+ if (ivVal) *iv_out = *ivVal;
return ciphertext;
}
@@ -751,6 +755,5 @@
}
} // namespace test
-} // namespace keymint
-} // namespace hardware
-} // namespace android
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
similarity index 89%
rename from keymint/aidl/vts/functional/KeyMintAidlTestBase.h
rename to security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 2948c41..f73c26d 100644
--- a/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -14,28 +14,23 @@
* limitations under the License.
*/
-#ifndef VTS_KEYMINT_AIDL_TEST_UTILS_H
-#define VTS_KEYMINT_AIDL_TEST_UTILS_H
-
#pragma once
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
-#include <android/hardware/keymint/ErrorCode.h>
-#include <android/hardware/keymint/IKeyMintDevice.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <gtest/gtest.h>
-#include <keymintSupport/authorization_set.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
-namespace android {
-namespace hardware {
-namespace keymint {
-namespace test {
+#include <keymint_support/authorization_set.h>
+
+namespace aidl::android::hardware::security::keymint::test {
using ::android::sp;
-using binder::Status;
+using Status = ::ndk::ScopedAStatus;
using ::std::shared_ptr;
using ::std::string;
using ::std::vector;
@@ -54,12 +49,12 @@
AbortIfNeeded();
}
- void InitializeKeyMint(sp<IKeyMintDevice> keyMint);
+ void InitializeKeyMint(std::shared_ptr<IKeyMintDevice> keyMint);
IKeyMintDevice& keyMint() { return *keymint_; }
uint32_t os_version() { return os_version_; }
uint32_t os_patch_level() { return os_patch_level_; }
- ErrorCode GetReturnErrorCode(Status result);
+ ErrorCode GetReturnErrorCode(const Status& result);
ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
KeyCharacteristics* key_characteristics);
@@ -85,7 +80,7 @@
ErrorCode Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
const AuthorizationSet& in_params, AuthorizationSet* out_params,
- sp<IKeyMintOperation>& op);
+ std::shared_ptr<IKeyMintOperation>& op);
ErrorCode Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
const AuthorizationSet& in_params, AuthorizationSet* out_params);
ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
@@ -103,7 +98,7 @@
ErrorCode Finish(string* output) { return Finish(string(), output); }
ErrorCode Abort();
- ErrorCode Abort(const sp<IKeyMintOperation>& op);
+ ErrorCode Abort(const shared_ptr<IKeyMintOperation>& op);
void AbortIfNeeded();
string ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
@@ -164,17 +159,17 @@
vector<Digest> ValidDigests(bool withNone, bool withMD5);
static vector<string> build_params() {
- auto params = android::getAidlHalInstanceNames(IKeyMintDevice::descriptor);
+ auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor);
return params;
}
- sp<IKeyMintOperation> op_;
+ std::shared_ptr<IKeyMintOperation> op_;
vector<Certificate> certChain_;
vector<uint8_t> key_blob_;
KeyCharacteristics key_characteristics_;
private:
- sp<IKeyMintDevice> keymint_;
+ std::shared_ptr<IKeyMintDevice> keymint_;
uint32_t os_version_;
uint32_t os_patch_level_;
@@ -187,11 +182,6 @@
#define INSTANTIATE_KEYMINT_AIDL_TEST(name) \
INSTANTIATE_TEST_SUITE_P(PerInstance, name, \
testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
- android::PrintInstanceNameToString)
+ ::android::PrintInstanceNameToString)
-} // namespace test
-} // namespace keymint
-} // namespace hardware
-} // namespace android
-
-#endif // VTS_KEYMINT_AIDL_TEST_UTILS_H
+} // namespace aidl::android::hardware::security::keymint::test
diff --git a/keymint/aidl/vts/functional/keyMint1Test.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
similarity index 96%
rename from keymint/aidl/vts/functional/keyMint1Test.cpp
rename to security/keymint/aidl/vts/functional/KeyMintTest.cpp
index c2fa2f8..eeb7491 100644
--- a/keymint/aidl/vts/functional/keyMint1Test.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -26,36 +26,32 @@
#include <cutils/properties.h>
-#include <android/hardware/keymint/KeyFormat.h>
+#include <aidl/android/hardware/security/keymint/KeyFormat.h>
-#include <keymintSupport/attestation_record.h>
-#include <keymintSupport/key_param_output.h>
-#include <keymintSupport/openssl_utils.h>
+#include <keymint_support/attestation_record.h>
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/openssl_utils.h>
#include "KeyMintAidlTestBase.h"
static bool arm_deleteAllKeys = false;
static bool dump_Attestations = false;
-using android::hardware::keymint::AuthorizationSet;
-using android::hardware::keymint::KeyCharacteristics;
-using android::hardware::keymint::KeyFormat;
+using aidl::android::hardware::security::keymint::AuthorizationSet;
+using aidl::android::hardware::security::keymint::KeyCharacteristics;
+using aidl::android::hardware::security::keymint::KeyFormat;
-namespace android {
-namespace hardware {
-
-namespace keymint {
+namespace aidl::android::hardware::security::keymint {
bool operator==(const keymint::AuthorizationSet& a, const keymint::AuthorizationSet& b) {
return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
}
-} // namespace keymint
-} // namespace hardware
-} // namespace android
+
+} // namespace aidl::android::hardware::security::keymint
namespace std {
-using namespace android::hardware::keymint;
+using namespace aidl::android::hardware::security::keymint;
template <>
struct std::equal_to<KeyCharacteristics> {
@@ -77,16 +73,17 @@
} // namespace std
-namespace android {
-namespace hardware {
-namespace keymint {
-namespace test {
+namespace aidl::android::hardware::security::keymint::test {
+
namespace {
template <TagType tag_type, Tag tag, typename ValueT>
bool contains(vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag, ValueT expected_value) {
auto it = std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) {
- return param.tag == tag && accessTagValue(ttag, param) == expected_value;
+ if (auto p = authorizationValue(ttag, param)) {
+ return *p == expected_value;
+ }
+ return false;
});
return (it != set.end());
}
@@ -257,10 +254,10 @@
EXPECT_TRUE(auths.Contains(TAG_OS_VERSION, os_version()))
<< "OS version is " << os_version() << " key reported "
- << auths.GetTagValue(TAG_OS_VERSION);
+ << auths.GetTagValue(TAG_OS_VERSION)->get();
EXPECT_TRUE(auths.Contains(TAG_OS_PATCHLEVEL, os_patch_level()))
<< "OS patch level is " << os_patch_level() << " key reported "
- << auths.GetTagValue(TAG_OS_PATCHLEVEL);
+ << auths.GetTagValue(TAG_OS_PATCHLEVEL)->get();
}
};
@@ -840,7 +837,7 @@
EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort());
// Set to sentinel, so TearDown() doesn't try to abort again.
- op_.clear();
+ op_.reset();
}
/*
@@ -2059,6 +2056,107 @@
}
/*
+ * EncryptionOperationsTest.RsaOaepWithMGFDigestSuccess
+ *
+ * Verifies that RSA-OAEP encryption operations work, with all SHA 256 digests and all type of MGF1
+ * digests.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepWithMGFDigestSuccess) {
+ auto digests = ValidDigests(false /* withNone */, true /* withMD5 */);
+
+ size_t key_size = 2048; // Need largish key for SHA-512 test.
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .OaepMGFDigest(digests)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(key_size, 65537)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(Digest::SHA_2_256)));
+
+ string message = "Hello";
+
+ for (auto digest : digests) {
+ auto params = AuthorizationSetBuilder()
+ .Authorization(TAG_RSA_OAEP_MGF_DIGEST, digest)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_OAEP);
+ string ciphertext1 = EncryptMessage(message, params);
+ if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
+ EXPECT_EQ(key_size / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(message, params);
+ EXPECT_EQ(key_size / 8, ciphertext2.size());
+
+ // OAEP randomizes padding so every result should be different (with astronomically high
+ // probability).
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext1 = DecryptMessage(ciphertext1, params);
+ EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest;
+ string plaintext2 = DecryptMessage(ciphertext2, params);
+ EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest;
+
+ // Decrypting corrupted ciphertext should fail.
+ size_t offset_to_corrupt = random() % ciphertext1.size();
+ char corrupt_byte;
+ do {
+ corrupt_byte = static_cast<char>(random() % 256);
+ } while (corrupt_byte == ciphertext1[offset_to_corrupt]);
+ ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+ string result;
+ EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+ EXPECT_EQ(0U, result.size());
+ }
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepWithMGFIncompatibleDigest
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate
+ * with incompatible MGF digest.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepWithMGFIncompatibleDigest) {
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(2048, 65537)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(Digest::SHA_2_256)));
+ string message = "Hello World!";
+
+ auto params = AuthorizationSetBuilder()
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_224);
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_MGF_DIGEST, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepWithMGFUnsupportedDigest
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate
+ * with unsupported MGF digest.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepWithMGFUnsupportedDigest) {
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(2048, 65537)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(Digest::SHA_2_256)));
+ string message = "Hello World!";
+
+ auto params = AuthorizationSetBuilder()
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::NONE);
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_MGF_DIGEST, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
* EncryptionOperationsTest.RsaPkcs1Success
*
* Verifies that RSA PKCS encryption/decrypts works.
@@ -2339,8 +2437,8 @@
vector<uint8_t> CopyIv(const AuthorizationSet& set) {
auto iv = set.GetTagValue(TAG_NONCE);
- EXPECT_TRUE(iv.isOk());
- return iv.value();
+ EXPECT_TRUE(iv);
+ return iv->get();
}
/*
@@ -2465,13 +2563,13 @@
case BlockMode::CBC:
case BlockMode::GCM:
case BlockMode::CTR:
- ASSERT_TRUE(iv.isOk()) << "No IV for block mode " << block_mode;
- EXPECT_EQ(block_mode == BlockMode::GCM ? 12U : 16U, iv.value().size());
- params.push_back(TAG_NONCE, iv.value());
+ ASSERT_TRUE(iv) << "No IV for block mode " << block_mode;
+ EXPECT_EQ(block_mode == BlockMode::GCM ? 12U : 16U, iv->get().size());
+ params.push_back(TAG_NONCE, iv->get());
break;
case BlockMode::ECB:
- EXPECT_FALSE(iv.isOk()) << "ECB mode should not generate IV";
+ EXPECT_FALSE(iv) << "ECB mode should not generate IV";
break;
}
@@ -2655,9 +2753,9 @@
AuthorizationSet out_params;
string ciphertext = EncryptMessage(message, params, &out_params);
EXPECT_EQ(message.size(), ciphertext.size());
- EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE).value().size());
+ EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE)->get().size());
- params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE).value());
+ params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE)->get());
string plaintext = DecryptMessage(ciphertext, params);
EXPECT_EQ(message, plaintext);
@@ -2703,9 +2801,9 @@
AuthorizationSet out_params;
string ciphertext = EncryptMessage(message, params, &out_params);
EXPECT_EQ(message.size(), ciphertext.size());
- EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE).value().size());
+ EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE)->get().size());
- params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE).value());
+ params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE)->get());
string plaintext = DecryptMessage(ciphertext, params);
EXPECT_EQ(message, plaintext);
@@ -2899,7 +2997,7 @@
AuthorizationSet begin_out_params;
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
EXPECT_EQ(1U, begin_out_params.size());
- ASSERT_TRUE(begin_out_params.GetTagValue(TAG_NONCE).isOk());
+ ASSERT_TRUE(begin_out_params.GetTagValue(TAG_NONCE));
AuthorizationSet finish_out_params;
string ciphertext;
@@ -3121,7 +3219,7 @@
EXPECT_EQ(ErrorCode::INVALID_TAG,
Update(update_params, "", &update_out_params, &ciphertext, &input_consumed));
- op_.clear();
+ op_.reset();
}
/*
@@ -3979,7 +4077,7 @@
auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
constexpr size_t max_operations = 100; // set to arbituary large number
- sp<IKeyMintOperation> op_handles[max_operations];
+ std::shared_ptr<IKeyMintOperation> op_handles[max_operations];
AuthorizationSet out_params;
ErrorCode result;
size_t i;
@@ -4046,10 +4144,7 @@
INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest);
-} // namespace test
-} // namespace keymint
-} // namespace hardware
-} // namespace android
+} // namespace aidl::android::hardware::security::keymint::test
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
@@ -4063,7 +4158,5 @@
}
}
}
- int status = RUN_ALL_TESTS();
- ALOGI("Test result = %d", status);
- return status;
+ return RUN_ALL_TESTS();
}
diff --git a/keymint/aidl/vts/functional/VerificationTokenTest.cpp b/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp
similarity index 97%
rename from keymint/aidl/vts/functional/VerificationTokenTest.cpp
rename to security/keymint/aidl/vts/functional/VerificationTokenTest.cpp
index bd0942b..0b1eccd 100644
--- a/keymint/aidl/vts/functional/VerificationTokenTest.cpp
+++ b/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp
@@ -16,10 +16,7 @@
#include "KeyMintAidlTestBase.h"
-namespace android {
-namespace hardware {
-namespace keymint {
-namespace test {
+namespace aidl::android::hardware::security::keymint::test {
class VerificationTokenTest : public KeyMintAidlTestBase {
protected:
@@ -168,7 +165,4 @@
INSTANTIATE_KEYMINT_AIDL_TEST(VerificationTokenTest);
-} // namespace test
-} // namespace keymint
-} // namespace hardware
-} // namespace android
+} // namespace aidl::android::hardware::security::keymint::test
diff --git a/keymint/support/Android.bp b/security/keymint/support/Android.bp
similarity index 90%
rename from keymint/support/Android.bp
rename to security/keymint/support/Android.bp
index 432416e..0cfa798 100644
--- a/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -15,7 +15,7 @@
//
cc_library {
- name: "libkeymintSupport",
+ name: "libkeymint_support",
cflags: [
"-Wall",
"-Wextra",
@@ -31,7 +31,7 @@
"include",
],
shared_libs: [
- "android.hardware.keymint-cpp",
+ "android.hardware.security.keymint-unstable-ndk_platform",
"libbase",
"libcrypto",
"libutils",
diff --git a/keymint/support/OWNERS b/security/keymint/support/OWNERS
similarity index 100%
rename from keymint/support/OWNERS
rename to security/keymint/support/OWNERS
diff --git a/keymint/support/attestation_record.cpp b/security/keymint/support/attestation_record.cpp
similarity index 95%
rename from keymint/support/attestation_record.cpp
rename to security/keymint/support/attestation_record.cpp
index e565974..596b097 100644
--- a/keymint/support/attestation_record.cpp
+++ b/security/keymint/support/attestation_record.cpp
@@ -14,27 +14,26 @@
* limitations under the License.
*/
-#include <keymintSupport/attestation_record.h>
+#include <keymint_support/attestation_record.h>
-#include <android/hardware/keymint/Tag.h>
-#include <android/hardware/keymint/TagType.h>
+#include <assert.h>
+
+#include <aidl/android/hardware/security/keymint/Tag.h>
+#include <aidl/android/hardware/security/keymint/TagType.h>
#include <android-base/logging.h>
-#include <assert.h>
#include <openssl/asn1t.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
-#include <keymintSupport/authorization_set.h>
-#include <keymintSupport/openssl_utils.h>
+#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
#define AT __FILE__ ":" << __LINE__
-namespace android {
-namespace hardware {
-namespace keymint {
+namespace aidl::android::hardware::security::keymint {
struct stack_st_ASN1_TYPE_Delete {
void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); }
@@ -327,9 +326,8 @@
}
ErrorCode parse_root_of_trust(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
- vector<uint8_t>* verified_boot_key,
- keymint_verified_boot_t* verified_boot_state, bool* device_locked,
- vector<uint8_t>* verified_boot_hash) {
+ vector<uint8_t>* verified_boot_key, VerifiedBoot* verified_boot_state,
+ bool* device_locked, vector<uint8_t>* verified_boot_hash) {
if (!verified_boot_key || !verified_boot_state || !device_locked || !verified_boot_hash) {
LOG(ERROR) << AT << "null pointer input(s)";
return ErrorCode::INVALID_ARGUMENT;
@@ -359,8 +357,8 @@
verified_boot_key->resize(vb_key->length);
memcpy(verified_boot_key->data(), vb_key->data, vb_key->length);
- *verified_boot_state = static_cast<keymint_verified_boot_t>(
- ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
+ *verified_boot_state =
+ static_cast<VerifiedBoot>(ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
if (!verified_boot_state) {
LOG(ERROR) << AT << " Failed verified boot state parsing";
return ErrorCode::INVALID_ARGUMENT;
@@ -382,6 +380,4 @@
return ErrorCode::OK; // KM_ERROR_OK;
}
-} // namespace keymint
-} // namespace hardware
-} // namespace android
+} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/authorization_set.cpp b/security/keymint/support/authorization_set.cpp
new file mode 100644
index 0000000..3d44dff
--- /dev/null
+++ b/security/keymint/support/authorization_set.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright 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.
+ */
+
+#include <keymint_support/authorization_set.h>
+
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/KeyParameter.h>
+#include <aidl/android/hardware/security/keymint/KeyPurpose.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+void AuthorizationSet::Sort() {
+ std::sort(data_.begin(), data_.end());
+}
+
+void AuthorizationSet::Deduplicate() {
+ if (data_.empty()) return;
+
+ Sort();
+ std::vector<KeyParameter> result;
+
+ auto curr = data_.begin();
+ auto prev = curr++;
+ for (; curr != data_.end(); ++prev, ++curr) {
+ if (prev->tag == Tag::INVALID) continue;
+
+ if (*prev != *curr) {
+ result.push_back(std::move(*prev));
+ }
+ }
+ result.push_back(std::move(*prev));
+
+ std::swap(data_, result);
+}
+
+void AuthorizationSet::Union(const AuthorizationSet& other) {
+ data_.insert(data_.end(), other.data_.begin(), other.data_.end());
+ Deduplicate();
+}
+
+void AuthorizationSet::Subtract(const AuthorizationSet& other) {
+ Deduplicate();
+
+ auto i = other.begin();
+ while (i != other.end()) {
+ int pos = -1;
+ do {
+ pos = find(i->tag, pos);
+ if (pos != -1 && (*i == data_[pos])) {
+ data_.erase(data_.begin() + pos);
+ break;
+ }
+ } while (pos != -1);
+ ++i;
+ }
+}
+
+KeyParameter& AuthorizationSet::operator[](int at) {
+ return data_[at];
+}
+
+const KeyParameter& AuthorizationSet::operator[](int at) const {
+ return data_[at];
+}
+
+void AuthorizationSet::Clear() {
+ data_.clear();
+}
+
+size_t AuthorizationSet::GetTagCount(Tag tag) const {
+ size_t count = 0;
+ for (int pos = -1; (pos = find(tag, pos)) != -1;) ++count;
+ return count;
+}
+
+int AuthorizationSet::find(Tag tag, int begin) const {
+ auto iter = data_.begin() + (1 + begin);
+
+ while (iter != data_.end() && iter->tag != tag) ++iter;
+
+ if (iter != data_.end()) return iter - data_.begin();
+ return -1;
+}
+
+bool AuthorizationSet::erase(int index) {
+ auto pos = data_.begin() + index;
+ if (pos != data_.end()) {
+ data_.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+std::optional<std::reference_wrapper<const KeyParameter>> AuthorizationSet::GetEntry(
+ Tag tag) const {
+ int pos = find(tag);
+ if (pos == -1) return {};
+ return std::reference_wrapper(data_[pos]);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size,
+ uint64_t public_exponent) {
+ Authorization(TAG_ALGORITHM, Algorithm::RSA);
+ Authorization(TAG_KEY_SIZE, key_size);
+ Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent);
+ return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) {
+ Authorization(TAG_ALGORITHM, Algorithm::EC);
+ Authorization(TAG_KEY_SIZE, key_size);
+ return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(EcCurve curve) {
+ Authorization(TAG_ALGORITHM, Algorithm::EC);
+ Authorization(TAG_EC_CURVE, curve);
+ return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) {
+ Authorization(TAG_ALGORITHM, Algorithm::AES);
+ return Authorization(TAG_KEY_SIZE, key_size);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesKey(uint32_t key_size) {
+ Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES);
+ return Authorization(TAG_KEY_SIZE, key_size);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) {
+ Authorization(TAG_ALGORITHM, Algorithm::HMAC);
+ Authorization(TAG_KEY_SIZE, key_size);
+ return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size,
+ uint64_t public_exponent) {
+ RsaKey(key_size, public_exponent);
+ return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size,
+ uint64_t public_exponent) {
+ RsaKey(key_size, public_exponent);
+ return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) {
+ EcdsaKey(key_size);
+ return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) {
+ EcdsaKey(curve);
+ return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) {
+ AesKey(key_size);
+ return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesEncryptionKey(uint32_t key_size) {
+ TripleDesKey(key_size);
+ return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() {
+ Authorization(TAG_PURPOSE, KeyPurpose::SIGN);
+ return Authorization(TAG_PURPOSE, KeyPurpose::VERIFY);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() {
+ Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT);
+ return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() {
+ Authorization(TAG_DIGEST, Digest::NONE);
+ return Authorization(TAG_PADDING, PaddingMode::NONE);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() {
+ return Authorization(TAG_BLOCK_MODE, BlockMode::ECB);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMinMacLen(uint32_t minMacLength) {
+ return BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, minMacLength);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMacLen(uint32_t macLength) {
+ return BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, macLength);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::BlockMode(
+ std::initializer_list<aidl::android::hardware::security::keymint::BlockMode> blockModes) {
+ for (auto mode : blockModes) {
+ push_back(TAG_BLOCK_MODE, mode);
+ }
+ return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::Digest(std::vector<keymint::Digest> digests) {
+ for (auto digest : digests) {
+ push_back(TAG_DIGEST, digest);
+ }
+ return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::OaepMGFDigest(
+ const std::vector<android::hardware::security::keymint::Digest>& digests) {
+ for (auto digest : digests) {
+ push_back(TAG_RSA_OAEP_MGF_DIGEST, digest);
+ }
+ return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::Padding(
+ std::initializer_list<PaddingMode> paddingModes) {
+ for (auto paddingMode : paddingModes) {
+ push_back(TAG_PADDING, paddingMode);
+ }
+ return *this;
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/keymint/support/include/keymintSupport/attestation_record.h b/security/keymint/support/include/keymint_support/attestation_record.h
similarity index 74%
rename from keymint/support/include/keymintSupport/attestation_record.h
rename to security/keymint/support/include/keymint_support/attestation_record.h
index 7a69789..bc76c93 100644
--- a/keymint/support/include/keymintSupport/attestation_record.h
+++ b/security/keymint/support/include/keymint_support/attestation_record.h
@@ -16,20 +16,14 @@
#pragma once
-#include <android/hardware/keymint/ErrorCode.h>
-#include <android/hardware/keymint/IKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
-#include <keymintSupport/attestation_record.h>
-#include <keymintSupport/authorization_set.h>
-#include <keymintSupport/openssl_utils.h>
+#include <keymint_support/attestation_record.h>
+#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
-namespace android {
-namespace hardware {
-namespace keymint {
-
-using android::hardware::keymint::KeyParameter;
-using android::hardware::keymint::Tag;
-using android::hardware::keymint::TAG_ALGORITHM;
+namespace aidl::android::hardware::security::keymint {
class AuthorizationSet;
@@ -49,18 +43,18 @@
*/
static const char kAttestionRecordOid[] = "1.3.6.1.4.1.11129.2.1.17";
-enum keymint_verified_boot_t {
- KM_VERIFIED_BOOT_VERIFIED = 0,
- KM_VERIFIED_BOOT_SELF_SIGNED = 1,
- KM_VERIFIED_BOOT_UNVERIFIED = 2,
- KM_VERIFIED_BOOT_FAILED = 3,
+enum class VerifiedBoot : uint8_t {
+ VERIFIED = 0,
+ SELF_SIGNED = 1,
+ UNVERIFIED = 2,
+ FAILED = 3,
};
struct RootOfTrust {
SecurityLevel security_level;
vector<uint8_t> verified_boot_key;
vector<uint8_t> verified_boot_hash;
- keymint_verified_boot_t verified_boot_state;
+ VerifiedBoot verified_boot_state;
bool device_locked;
};
@@ -87,9 +81,7 @@
ErrorCode parse_root_of_trust(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
std::vector<uint8_t>* verified_boot_key,
- keymint_verified_boot_t* verified_boot_state, bool* device_locked,
+ VerifiedBoot* verified_boot_state, bool* device_locked,
std::vector<uint8_t>* verified_boot_hash);
-} // namespace keymint
-} // namespace hardware
-} // namespace android
+} // namespace aidl::android::hardware::security::keymint
diff --git a/keymint/support/include/keymintSupport/authorization_set.h b/security/keymint/support/include/keymint_support/authorization_set.h
similarity index 83%
rename from keymint/support/include/keymintSupport/authorization_set.h
rename to security/keymint/support/include/keymint_support/authorization_set.h
index 141426a..596bb89 100644
--- a/keymint/support/include/keymintSupport/authorization_set.h
+++ b/security/keymint/support/include/keymint_support/authorization_set.h
@@ -14,35 +14,26 @@
* limitations under the License.
*/
-#ifndef SYSTEM_SECURITY_KEYSTORE_KM4_AUTHORIZATION_SET_H_
-#define SYSTEM_SECURITY_KEYSTORE_KM4_AUTHORIZATION_SET_H_
+#pragma once
#include <vector>
-#include <android/hardware/keymint/BlockMode.h>
-#include <android/hardware/keymint/Digest.h>
-#include <android/hardware/keymint/EcCurve.h>
-#include <android/hardware/keymint/PaddingMode.h>
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
-#include <keymintSupport/keymint_tags.h>
+#include <keymint_support/keymint_tags.h>
-namespace android {
-namespace hardware {
-namespace keymint {
-
-using android::hardware::keymint::BlockMode;
-using android::hardware::keymint::Digest;
-using android::hardware::keymint::EcCurve;
-using android::hardware::keymint::PaddingMode;
+namespace aidl::android::hardware::security::keymint {
using std::vector;
class AuthorizationSetBuilder;
/**
- * An ordered collection of KeyParameters. It provides memory ownership and some convenient
- * functionality for sorting, deduplicating, joining, and subtracting sets of KeyParameters.
- * For serialization, wrap the backing store of this structure in a vector<KeyParameter>.
+ * A collection of KeyParameters. It provides memory ownership and some convenient functionality for
+ * sorting, deduplicating, joining, and subtracting sets of KeyParameters.
*/
class AuthorizationSet {
public:
@@ -145,19 +136,16 @@
/**
* Returns iterator (pointer) to beginning of elems array, to enable STL-style iteration
*/
- std::vector<KeyParameter>::const_iterator begin() const { return data_.begin(); }
+ auto begin() { return data_.begin(); }
+ auto begin() const { return data_.begin(); }
/**
* Returns iterator (pointer) one past end of elems array, to enable STL-style iteration
*/
- std::vector<KeyParameter>::const_iterator end() const { return data_.end(); }
+ auto end() { return data_.end(); }
+ auto end() const { return data_.end(); }
/**
- * Modifies this Authorization set such that it only keeps the entries for which doKeep
- * returns true.
- */
- void Filter(std::function<bool(const KeyParameter&)> doKeep);
- /**
* Returns the nth element of the set.
* Like for std::vector::operator[] there is no range check performed. Use of out of range
* indices is undefined.
@@ -180,7 +168,7 @@
bool Contains(TypedTag<tag_type, tag> ttag, const ValueT& value) const {
for (const auto& param : data_) {
auto entry = authorizationValue(ttag, param);
- if (entry.isOk() && static_cast<ValueT>(entry.value()) == value) return true;
+ if (entry && static_cast<ValueT>(*entry) == value) return true;
}
return false;
}
@@ -190,9 +178,9 @@
size_t GetTagCount(Tag tag) const;
template <typename T>
- inline NullOr<const typename TypedTag2ValueType<T>::type&> GetTagValue(T tag) const {
+ inline auto GetTagValue(T tag) const -> decltype(authorizationValue(tag, KeyParameter())) {
auto entry = GetEntry(tag);
- if (entry.isOk()) return authorizationValue(tag, entry.value());
+ if (entry) return authorizationValue(tag, *entry);
return {};
}
@@ -230,11 +218,8 @@
return result;
}
- void Serialize(std::ostream* out) const;
- void Deserialize(std::istream* in);
-
private:
- NullOr<const KeyParameter&> GetEntry(Tag tag) const;
+ std::optional<std::reference_wrapper<const KeyParameter>> GetEntry(Tag tag) const;
std::vector<KeyParameter> data_;
};
@@ -305,6 +290,7 @@
AuthorizationSetBuilder& GcmModeMacLen(uint32_t macLength);
AuthorizationSetBuilder& BlockMode(std::initializer_list<BlockMode> blockModes);
+ AuthorizationSetBuilder& OaepMGFDigest(const std::vector<Digest>& digests);
AuthorizationSetBuilder& Digest(std::vector<Digest> digests);
AuthorizationSetBuilder& Padding(std::initializer_list<PaddingMode> paddings);
@@ -322,8 +308,4 @@
}
};
-} // namespace keymint
-} // namespace hardware
-} // namespace android
-
-#endif // SYSTEM_SECURITY_KEYSTORE_KM4_AUTHORIZATION_SET_H_
+} // namespace aidl::android::hardware::security::keymint
diff --git a/keymint/support/include/keymintSupport/key_param_output.h b/security/keymint/support/include/keymint_support/key_param_output.h
similarity index 64%
rename from keymint/support/include/keymintSupport/key_param_output.h
rename to security/keymint/support/include/keymint_support/key_param_output.h
index a35a981..5f004fe 100644
--- a/keymint/support/include/keymintSupport/key_param_output.h
+++ b/security/keymint/support/include/keymint_support/key_param_output.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,34 +14,29 @@
* limitations under the License.
*/
-#ifndef HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEY_PARAM_OUTPUT_H_
-#define HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEY_PARAM_OUTPUT_H_
+#pragma once
#include <iostream>
#include <vector>
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthenticatorType.h>
+#include <aidl/android/hardware/security/keymint/KeyCharacteristics.h>
+#include <aidl/android/hardware/security/keymint/KeyOrigin.h>
+#include <aidl/android/hardware/security/keymint/KeyParameter.h>
+#include <aidl/android/hardware/security/keymint/KeyPurpose.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+#include <aidl/android/hardware/security/keymint/Tag.h>
+#include <aidl/android/hardware/security/keymint/TagType.h>
+
#include "keymint_tags.h"
-#include <android/hardware/keymint/Algorithm.h>
-#include <android/hardware/keymint/BlockMode.h>
-#include <android/hardware/keymint/Digest.h>
-#include <android/hardware/keymint/EcCurve.h>
-#include <android/hardware/keymint/ErrorCode.h>
-#include <android/hardware/keymint/HardwareAuthenticatorType.h>
-#include <android/hardware/keymint/KeyCharacteristics.h>
-#include <android/hardware/keymint/KeyOrigin.h>
-#include <android/hardware/keymint/KeyParameter.h>
-#include <android/hardware/keymint/KeyPurpose.h>
-#include <android/hardware/keymint/PaddingMode.h>
-#include <android/hardware/keymint/SecurityLevel.h>
-#include <android/hardware/keymint/Tag.h>
-#include <android/hardware/keymint/TagType.h>
-
-namespace android {
-namespace hardware {
-namespace keymint {
-
-using namespace ::android::hardware::keymint;
+namespace aidl::android::hardware::security::keymint {
inline ::std::ostream& operator<<(::std::ostream& os, Algorithm value) {
return os << toString(value);
@@ -76,7 +71,7 @@
}
template <typename ValueT>
-::std::ostream& operator<<(::std::ostream& os, const NullOr<ValueT>& value) {
+::std::ostream& operator<<(::std::ostream& os, const std::optional<ValueT>& value) {
if (!value.isOk()) {
os << "(value not present)";
} else {
@@ -101,8 +96,4 @@
return os << toString(tag);
}
-} // namespace keymint
-} // namespace hardware
-} // namespace android
-
-#endif // HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEY_PARAM_OUTPUT_H_
+} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/keymint_tags.h b/security/keymint/support/include/keymint_support/keymint_tags.h
new file mode 100644
index 0000000..76aecb7
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/keymint_tags.h
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthenticatorType.h>
+#include <aidl/android/hardware/security/keymint/KeyOrigin.h>
+#include <aidl/android/hardware/security/keymint/KeyParameter.h>
+#include <aidl/android/hardware/security/keymint/KeyPurpose.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+#include <aidl/android/hardware/security/keymint/Tag.h>
+#include <aidl/android/hardware/security/keymint/TagType.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+constexpr TagType typeFromTag(Tag tag) {
+ return static_cast<TagType>(static_cast<uint32_t>(tag) & static_cast<uint32_t>(0xf0000000));
+}
+
+/**
+ * TypedTag is a templatized version of Tag, which provides compile-time checking of KeyMint tag
+ * types. Instances are convertible to Tag, so they can be used wherever Tag is expected, and
+ * because they encode the tag type it's possible to create function overloads that only operate on
+ * tags with a particular type.
+ */
+template <TagType tag_type, Tag tag>
+struct TypedTag {
+ inline TypedTag() {
+ // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type
+ // 'tag_type'. Attempting to instantiate a tag with the wrong type will result in a compile
+ // error (no match for template specialization StaticAssert<false>), with no run-time cost.
+ static_assert(typeFromTag(tag) == tag_type, "mismatch between tag and tag_type");
+ }
+ operator Tag() const { return tag; }
+ int32_t maskedTag() { return static_cast<uint32_t>(tag) & 0x0FFFFFFF; }
+};
+
+template <Tag tag>
+struct Tag2TypedTag {
+ typedef TypedTag<typeFromTag(tag), tag> type;
+};
+
+#ifdef DECLARE_TYPED_TAG
+#undef DECLARE_TYPED_TAG
+#endif
+
+#define DECLARE_TYPED_TAG(name) \
+ typedef typename Tag2TypedTag<Tag::name>::type TAG_##name##_t; \
+ static TAG_##name##_t TAG_##name;
+
+DECLARE_TYPED_TAG(ACTIVE_DATETIME);
+DECLARE_TYPED_TAG(ALGORITHM);
+DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
+DECLARE_TYPED_TAG(APPLICATION_DATA);
+DECLARE_TYPED_TAG(APPLICATION_ID);
+DECLARE_TYPED_TAG(ASSOCIATED_DATA);
+DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
+DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
+DECLARE_TYPED_TAG(ATTESTATION_ID_BRAND);
+DECLARE_TYPED_TAG(ATTESTATION_ID_DEVICE);
+DECLARE_TYPED_TAG(ATTESTATION_ID_IMEI);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MANUFACTURER);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MEID);
+DECLARE_TYPED_TAG(ATTESTATION_ID_PRODUCT);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MODEL);
+DECLARE_TYPED_TAG(ATTESTATION_ID_SERIAL);
+DECLARE_TYPED_TAG(AUTH_TIMEOUT);
+DECLARE_TYPED_TAG(BLOCK_MODE);
+DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
+DECLARE_TYPED_TAG(BOOT_PATCHLEVEL);
+DECLARE_TYPED_TAG(CALLER_NONCE);
+DECLARE_TYPED_TAG(CONFIRMATION_TOKEN);
+DECLARE_TYPED_TAG(CREATION_DATETIME);
+DECLARE_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION);
+DECLARE_TYPED_TAG(DIGEST);
+DECLARE_TYPED_TAG(EARLY_BOOT_ONLY);
+DECLARE_TYPED_TAG(EC_CURVE);
+DECLARE_TYPED_TAG(HARDWARE_TYPE);
+DECLARE_TYPED_TAG(IDENTITY_CREDENTIAL_KEY);
+DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
+DECLARE_TYPED_TAG(INVALID);
+DECLARE_TYPED_TAG(KEY_SIZE);
+DECLARE_TYPED_TAG(MAC_LENGTH);
+DECLARE_TYPED_TAG(MAX_USES_PER_BOOT);
+DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
+DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
+DECLARE_TYPED_TAG(NONCE);
+DECLARE_TYPED_TAG(NO_AUTH_REQUIRED);
+DECLARE_TYPED_TAG(ORIGIN);
+DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(OS_PATCHLEVEL);
+DECLARE_TYPED_TAG(OS_VERSION);
+DECLARE_TYPED_TAG(PADDING);
+DECLARE_TYPED_TAG(PURPOSE);
+DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
+DECLARE_TYPED_TAG(ROLLBACK_RESISTANCE);
+DECLARE_TYPED_TAG(ROOT_OF_TRUST);
+DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
+DECLARE_TYPED_TAG(STORAGE_KEY);
+DECLARE_TYPED_TAG(TRUSTED_CONFIRMATION_REQUIRED);
+DECLARE_TYPED_TAG(TRUSTED_USER_PRESENCE_REQUIRED);
+DECLARE_TYPED_TAG(UNIQUE_ID);
+DECLARE_TYPED_TAG(UNLOCKED_DEVICE_REQUIRED);
+DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(USER_AUTH_TYPE);
+DECLARE_TYPED_TAG(USER_ID);
+DECLARE_TYPED_TAG(USER_SECURE_ID);
+DECLARE_TYPED_TAG(VENDOR_PATCHLEVEL);
+DECLARE_TYPED_TAG(RSA_OAEP_MGF_DIGEST);
+
+#undef DECLARE_TYPED_TAG
+
+template <typename... Elems>
+struct MetaList {};
+
+using all_tags_t = MetaList<
+ TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t,
+ TAG_RSA_PUBLIC_EXPONENT_t, TAG_INCLUDE_UNIQUE_ID_t, TAG_ACTIVE_DATETIME_t,
+ TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t,
+ TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_USER_ID_t, TAG_USER_SECURE_ID_t,
+ TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t, TAG_ALLOW_WHILE_ON_BODY_t,
+ TAG_UNLOCKED_DEVICE_REQUIRED_t, TAG_APPLICATION_ID_t, TAG_APPLICATION_DATA_t,
+ TAG_CREATION_DATETIME_t, TAG_ROLLBACK_RESISTANCE_t, TAG_HARDWARE_TYPE_t,
+ TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t, TAG_BOOTLOADER_ONLY_t,
+ TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t, TAG_ATTESTATION_CHALLENGE_t,
+ TAG_ATTESTATION_APPLICATION_ID_t, TAG_ATTESTATION_ID_BRAND_t, TAG_ATTESTATION_ID_DEVICE_t,
+ TAG_ATTESTATION_ID_PRODUCT_t, TAG_ATTESTATION_ID_MANUFACTURER_t, TAG_ATTESTATION_ID_MODEL_t,
+ TAG_ATTESTATION_ID_SERIAL_t, TAG_ATTESTATION_ID_IMEI_t, TAG_ATTESTATION_ID_MEID_t,
+ TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t,
+ TAG_DIGEST_t, TAG_PADDING_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t,
+ TAG_BOOT_PATCHLEVEL_t, TAG_VENDOR_PATCHLEVEL_t, TAG_TRUSTED_CONFIRMATION_REQUIRED_t,
+ TAG_TRUSTED_USER_PRESENCE_REQUIRED_t>;
+
+template <typename TypedTagType>
+struct TypedTag2ValueType;
+
+#ifdef MAKE_TAG_VALUE_ACCESSOR
+#undef MAKE_TAG_VALUE_ACCESSOR
+#endif
+
+#define MAKE_TAG_VALUE_ACCESSOR(tag_type, field_name) \
+ template <Tag tag> \
+ struct TypedTag2ValueType<TypedTag<tag_type, tag>> { \
+ using type = std::remove_reference< \
+ decltype(static_cast<KeyParameterValue*>(nullptr) \
+ ->get<KeyParameterValue::field_name>())>::type; \
+ static constexpr KeyParameterValue::Tag unionTag = KeyParameterValue::field_name; \
+ }; \
+ template <Tag tag> \
+ inline std::optional<std::reference_wrapper< \
+ const typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>> \
+ accessTagValue(TypedTag<tag_type, tag>, const KeyParameter& param) { \
+ if (param.value.getTag() == KeyParameterValue::field_name) { \
+ return std::optional( \
+ std::reference_wrapper(param.value.get<KeyParameterValue::field_name>())); \
+ } else { \
+ return std::nullopt; \
+ } \
+ } \
+ template <Tag tag> \
+ inline std::optional< \
+ std::reference_wrapper<typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>> \
+ accessTagValue(TypedTag<tag_type, tag>, KeyParameter& param) { \
+ if (param.value.getTag() == KeyParameterValue::field_name) { \
+ return std::optional( \
+ std::reference_wrapper(param.value.get<KeyParameterValue::field_name>())); \
+ } else { \
+ return std::nullopt; \
+ } \
+ }
+
+MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG, longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG_REP, longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::DATE, dateTime)
+MAKE_TAG_VALUE_ACCESSOR(TagType::UINT, integer)
+MAKE_TAG_VALUE_ACCESSOR(TagType::UINT_REP, integer)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BOOL, boolValue)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BYTES, blob)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BIGNUM, blob)
+
+#undef MAKE_TAG_VALUE_ACCESSOR
+
+#ifdef MAKE_TAG_ENUM_VALUE_ACCESSOR
+#undef MAKE_TAG_ENUM_VALUE_ACCESSOR
+#endif
+
+#define MAKE_TAG_ENUM_VALUE_ACCESSOR(typed_tag, field_name) \
+ template <> \
+ struct TypedTag2ValueType<decltype(typed_tag)> { \
+ using type = std::remove_reference< \
+ decltype(static_cast<KeyParameterValue*>(nullptr) \
+ ->get<KeyParameterValue::field_name>())>::type; \
+ static constexpr KeyParameterValue::Tag unionTag = KeyParameterValue::field_name; \
+ }; \
+ inline std::optional< \
+ std::reference_wrapper<const typename TypedTag2ValueType<decltype(typed_tag)>::type>> \
+ accessTagValue(decltype(typed_tag), const KeyParameter& param) { \
+ if (param.value.getTag() == KeyParameterValue::field_name) { \
+ return std::optional( \
+ std::reference_wrapper(param.value.get<KeyParameterValue::field_name>())); \
+ } else { \
+ return std::nullopt; \
+ } \
+ } \
+ inline std::optional< \
+ std::reference_wrapper<typename TypedTag2ValueType<decltype(typed_tag)>::type>> \
+ accessTagValue(decltype(typed_tag), KeyParameter& param) { \
+ if (param.value.getTag() == KeyParameterValue::field_name) { \
+ return std::optional( \
+ std::reference_wrapper(param.value.get<KeyParameterValue::field_name>())); \
+ } else { \
+ return std::nullopt; \
+ } \
+ }
+
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ALGORITHM, algorithm)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOCK_MODE, blockMode)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_DIGEST, digest)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_EC_CURVE, ecCurve)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ORIGIN, origin)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PADDING, paddingMode)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PURPOSE, keyPurpose)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_USER_AUTH_TYPE, hardwareAuthenticatorType)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_HARDWARE_TYPE, securityLevel)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_RSA_OAEP_MGF_DIGEST, digest)
+
+#undef MAKE_TAG_ENUM_VALUE_ACCESSOR
+
+template <TagType tag_type, Tag tag, typename ValueT>
+inline KeyParameter makeKeyParameter(TypedTag<tag_type, tag> ttag, ValueT&& value) {
+ KeyParameter retval;
+ retval.tag = tag;
+ retval.value = KeyParameterValue::make<TypedTag2ValueType<decltype(ttag)>::unionTag>(
+ std::forward<ValueT>(value));
+ return retval;
+}
+
+// the boolean case
+template <Tag tag>
+inline KeyParameter makeKeyParameter(TypedTag<TagType::BOOL, tag>) {
+ KeyParameter retval;
+ retval.tag = tag;
+ retval.value = KeyParameterValue::make<KeyParameterValue::boolValue>(true);
+ return retval;
+}
+
+// the invalid case
+inline KeyParameter makeKeyParameter(TypedTag<TagType::INVALID, Tag::INVALID>) {
+ KeyParameter retval;
+ retval.tag = Tag::INVALID;
+ retval.value = KeyParameterValue::make<KeyParameterValue::invalid>(0);
+ return retval;
+}
+
+template <typename... Pack>
+struct FirstOrNoneHelper;
+template <typename First>
+struct FirstOrNoneHelper<First> {
+ typedef First type;
+};
+template <>
+struct FirstOrNoneHelper<> {
+ struct type {};
+};
+
+template <typename... Pack>
+using FirstOrNone = typename FirstOrNoneHelper<Pack...>::type;
+
+template <TagType tag_type, Tag tag, typename... Args>
+inline KeyParameter Authorization(TypedTag<tag_type, tag> ttag, Args&&... args) {
+ static_assert(tag_type != TagType::BOOL || (sizeof...(args) == 0),
+ "TagType::BOOL Authorizations do not take parameters. Presence is truth.");
+ static_assert(tag_type == TagType::BOOL || (sizeof...(args) == 1),
+ "Authorization other then TagType::BOOL take exactly one parameter.");
+ static_assert(
+ tag_type == TagType::BOOL ||
+ std::is_convertible<
+ std::remove_cv_t<std::remove_reference_t<FirstOrNone<Args...>>>,
+ typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>::value,
+ "Invalid argument type for given tag.");
+
+ return makeKeyParameter(ttag, std::forward<Args>(args)...);
+}
+
+template <typename T>
+std::remove_reference_t<T> NullOrOr(T&& v) {
+ if (v) return v;
+ return {};
+}
+
+template <typename Head, typename... Tail>
+std::remove_reference_t<Head> NullOrOr(Head&& head, Tail&&... tail) {
+ if (head) return head;
+ return NullOrOr(std::forward<Tail>(tail)...);
+}
+
+template <typename Default, typename Wrapped>
+std::remove_reference_t<Wrapped> defaultOr(std::optional<Wrapped>&& optional, Default&& def) {
+ static_assert(std::is_convertible<std::remove_reference_t<Default>,
+ std::remove_reference_t<Wrapped>>::value,
+ "Type of default value must match the type wrapped by std::optional");
+ if (optional) return *optional;
+ return def;
+}
+
+template <TagType tag_type, Tag tag>
+inline std::optional<
+ std::reference_wrapper<const typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>>
+authorizationValue(TypedTag<tag_type, tag> ttag, const KeyParameter& param) {
+ if (TypedTag2ValueType<TypedTag<tag_type, tag>>::unionTag != param.value.getTag()) return {};
+ return accessTagValue(ttag, param);
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/keymint/support/include/keymintSupport/keymint_utils.h b/security/keymint/support/include/keymint_support/keymint_utils.h
similarity index 76%
rename from keymint/support/include/keymintSupport/keymint_utils.h
rename to security/keymint/support/include/keymint_support/keymint_utils.h
index aa1e93b..53d5b96 100644
--- a/keymint/support/include/keymintSupport/keymint_utils.h
+++ b/security/keymint/support/include/keymint_support/keymint_utils.h
@@ -16,14 +16,9 @@
#pragma once
-#ifndef HARDWARE_INTERFACES_KEYMINT_10_SUPPORT_KEYMINT_UTILS_H_
-#define HARDWARE_INTERFACES_KEYMINT_10_SUPPORT_KEYMINT_UTILS_H_
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
-#include <android/hardware/keymint/HardwareAuthToken.h>
-
-namespace android {
-namespace hardware {
-namespace keymint {
+namespace aidl::android::hardware::security::keymint {
using std::vector;
@@ -44,8 +39,4 @@
uint32_t getOsVersion();
uint32_t getOsPatchlevel();
-} // namespace keymint
-} // namespace hardware
-} // namespace android
-
-#endif // HARDWARE_INTERFACES_KEYMINT_10_SUPPORT_KEYMINT_UTILS_H_
+} // namespace aidl::android::hardware::security::keymint
diff --git a/keymint/support/include/keymintSupport/openssl_utils.h b/security/keymint/support/include/keymint_support/openssl_utils.h
similarity index 64%
rename from keymint/support/include/keymintSupport/openssl_utils.h
rename to security/keymint/support/include/keymint_support/openssl_utils.h
index 39633ed..9ae7e52 100644
--- a/keymint/support/include/keymintSupport/openssl_utils.h
+++ b/security/keymint/support/include/keymint_support/openssl_utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#ifndef HARDWARE_INTERFACES_KEYMINT_1_0_SUPPORT_OPENSSL_UTILS_H_
-#define HARDWARE_INTERFACES_KEYMINT_1_0_SUPPORT_OPENSSL_UTILS_H_
+#pragma once
-#include <android/hardware/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
+namespace aidl::android::hardware::security::keymint {
+
template <typename T, void (*F)(T*)>
struct UniquePtrDeleter {
void operator()(T* p) const { F(p); }
@@ -40,24 +41,24 @@
typedef std::unique_ptr<BIGNUM, UniquePtrDeleter<BIGNUM, BN_free>> BIGNUM_Ptr;
-inline const EVP_MD* openssl_digest(android::hardware::keymint::Digest digest) {
+inline const EVP_MD* openssl_digest(Digest digest) {
switch (digest) {
- case android::hardware::keymint::Digest::NONE:
+ case Digest::NONE:
return nullptr;
- case android::hardware::keymint::Digest::MD5:
+ case Digest::MD5:
return EVP_md5();
- case android::hardware::keymint::Digest::SHA1:
+ case Digest::SHA1:
return EVP_sha1();
- case android::hardware::keymint::Digest::SHA_2_224:
+ case Digest::SHA_2_224:
return EVP_sha224();
- case android::hardware::keymint::Digest::SHA_2_256:
+ case Digest::SHA_2_256:
return EVP_sha256();
- case android::hardware::keymint::Digest::SHA_2_384:
+ case Digest::SHA_2_384:
return EVP_sha384();
- case android::hardware::keymint::Digest::SHA_2_512:
+ case Digest::SHA_2_512:
return EVP_sha512();
}
return nullptr;
}
-#endif // HARDWARE_INTERFACES_KEYMINT_1_0_SUPPORT_OPENSSL_UTILS_H_
+} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/key_param_output.cpp b/security/keymint/support/key_param_output.cpp
new file mode 100644
index 0000000..0950eb6
--- /dev/null
+++ b/security/keymint/support/key_param_output.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#include <keymint_support/key_param_output.h>
+
+#include <iomanip>
+
+#include <keymint_support/keymint_tags.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using ::std::endl;
+using ::std::ostream;
+
+ostream& operator<<(ostream& os, const ::std::vector<KeyParameter>& set) {
+ if (set.size() == 0) {
+ os << "(Empty)" << endl;
+ } else {
+ os << "\n";
+ for (const auto& elem : set) os << elem << endl;
+ }
+ return os;
+}
+
+ostream& operator<<(ostream& os, const KeyParameter& param) {
+ return os << param.toString();
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/keymint/support/keymint_utils.cpp b/security/keymint/support/keymint_utils.cpp
similarity index 94%
rename from keymint/support/keymint_utils.cpp
rename to security/keymint/support/keymint_utils.cpp
index fd57cf5..e73d602 100644
--- a/keymint/support/keymint_utils.cpp
+++ b/security/keymint/support/keymint_utils.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -18,11 +18,8 @@
#include <android-base/properties.h>
#include <hardware/hw_auth_token.h>
-#include <keymintSupport/keymint_utils.h>
-#include <arpa/inet.h>
-
-namespace android::hardware::keymint {
+namespace aidl::android::hardware::security::keymint {
namespace {
@@ -111,4 +108,4 @@
return getOsPatchlevel(patchlevel.c_str());
}
-} // namespace android::hardware::keymint
+} // namespace aidl::android::hardware::security::keymint
diff --git a/tests/lazy/1.1/ILazy.hal b/tests/lazy/1.1/ILazy.hal
index a15e0e3..b0a6a2a 100644
--- a/tests/lazy/1.1/ILazy.hal
+++ b/tests/lazy/1.1/ILazy.hal
@@ -18,4 +18,12 @@
import android.hardware.tests.lazy@1.0;
-interface ILazy extends @1.0::ILazy {};
+interface ILazy extends @1.0::ILazy {
+ /**
+ * Ask the process hosting the service to install a callback that notifies
+ * it when the number of active (i.e. with clients) services changes.
+ * For testing purposes, this callback exercises the code to unregister/re-register
+ * the services and eventually shuts down the process.
+ */
+ setCustomActiveServicesCountCallback();
+};