Merge "wifi: fix old vts tests on newer HAL"
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..414eede 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,43 @@
return isOutputDevice(stringToAudioDevice(device));
}
+static inline bool isVendorExtension(const std::string& device) {
+ // Must match the "vendorExtension" rule from the XSD file.
+ static const std::string vendorPrefix = "VX_";
+ return device.size() > vendorPrefix.size() &&
+ device.substr(0, vendorPrefix.size()) == vendorPrefix &&
+ std::all_of(device.begin() + vendorPrefix.size(), device.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 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 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/types.hal b/audio/common/7.0/types.hal
index 631d524..ed56c73 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;
};
@@ -328,14 +332,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 +355,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 +425,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 +455,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..1a66282
--- /dev/null
+++ b/audio/common/all-versions/default/7.0/HidlUtils.cpp
@@ -0,0 +1,847 @@
+/*
+ * 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/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::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::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::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);
+ CONVERT_CHECKED(audioOffloadInfoFromHal(halConfig.offload_info, &config->offloadInfo), 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;
+ CONVERT_CHECKED(audioOffloadInfoToHal(config.offloadInfo, &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::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..b83a58a 100644
--- a/audio/common/all-versions/default/Android.bp
+++ b/audio/common/all-versions/default/Android.bp
@@ -39,14 +39,19 @@
],
}
+filegroup {
+ name: "android.hardware.audio.common-util@2-6",
+ srcs: [
+ "HidlUtils.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 +74,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 +88,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 +102,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 +116,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 +127,18 @@
],
}
-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",
+ "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 +146,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..ab3c1c7 100644
--- a/audio/common/all-versions/default/HidlUtils.cpp
+++ b/audio/common/all-versions/default/HidlUtils.cpp
@@ -37,13 +37,14 @@
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,
@@ -57,8 +58,8 @@
config->rampDurationMs = halConfig.ramp_duration_ms;
}
-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,6 +68,7 @@
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) {
@@ -80,7 +82,7 @@
gain->maxRampMs = halGain.max_ramp_ms;
}
-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 +91,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 +125,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 +145,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 +161,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 +170,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);
@@ -201,10 +208,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 +250,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);
@@ -305,9 +296,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,22 +348,7 @@
break;
}
}
-}
-
-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());
-}
-
-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());
+ return NO_ERROR;
}
} // namespace implementation
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index ef6dee3..4e609ca 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -34,40 +34,123 @@
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.
+struct HidlUtils {
+#if MAJOR_VERSION < 7
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.
+#else
+ static status_t audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+ AudioConfig* config);
+ static status_t audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+ AudioGainConfig* config);
+ static status_t audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+ AudioGain* gain);
+#endif
+ static status_t audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
+ static status_t audioGainConfigToHal(const AudioGainConfig& config,
+ struct audio_gain_config* halConfig);
+ 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 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) {
+ 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;
+ }
+ static status_t 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;
+ }
+
+ // PLEASE DO NOT USE, will be removed in a couple of days
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);
+ const hidl_vec<AudioPortConfig>& configs) {
+ std::unique_ptr<audio_port_config[]> halConfigs;
+ (void)audioPortConfigsToHal(configs, &halConfigs);
+ return halConfigs;
+ }
+
+ static status_t audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
+ static status_t audioPortToHal(const AudioPort& port, struct audio_port* halPort);
+#if MAJOR_VERSION >= 7
+ static status_t audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+ AudioChannelMask* channelMask);
+ 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 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 audioSourceFromHal(audio_source_t halSource, AudioSource* source);
+ static status_t audioSourceToHal(const AudioSource& source, audio_source_t* halSource);
+ 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 deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+ char* halDeviceAddress);
+ static status_t deviceAddressFromHal(audio_devices_t halDeviceType,
+ const char* halDeviceAddress, DeviceAddress* device);
+
+ 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
};
} // namespace implementation
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..bfc99e6
--- /dev/null
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -0,0 +1,631 @@
+/*
+ * 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 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_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, 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, 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, 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);
+ config.offloadInfo.base = config.base;
+ config.offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
+ config.offloadInfo.bitRatePerSecond = 320;
+ config.offloadInfo.durationMicroseconds = -1;
+ config.offloadInfo.bitWidth = 16;
+ config.offloadInfo.bufferSize = 1024;
+ config.offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
+ config.offloadInfo.encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM;
+ config.offloadInfo.contentId = 42;
+ config.offloadInfo.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));
+}
diff --git a/audio/core/all-versions/default/Conversions.cpp b/audio/core/all-versions/default/Conversions.cpp
index 0db210a..28d8f78 100644
--- a/audio/core/all-versions/default/Conversions.cpp
+++ b/audio/core/all-versions/default/Conversions.cpp
@@ -27,6 +27,7 @@
namespace CPP_VERSION {
namespace implementation {
+// TODO(mnaganov): Use method from HidlUtils for V7
std::string deviceAddressToHal(const DeviceAddress& address) {
// HAL assumes that the address is NUL-terminated.
char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 6260ba1..3c28159 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -283,8 +283,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],
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..1612d3c 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -67,8 +67,7 @@
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;
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..941c4bd 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -26,8 +26,7 @@
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);
diff --git a/audio/effect/7.0/types.hal b/audio/effect/7.0/types.hal
index fe4ee51..b0a0709 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
};
@@ -270,21 +285,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. */
+ vec<AudioChannelMask> mainChannels;
+ /** Channel mask for auxiliary channels. */
+ vec<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/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/EffectsFactory.cpp b/audio/effect/all-versions/default/EffectsFactory.cpp
index acce7de..b265d3d 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,
@@ -135,7 +135,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 +170,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/biometrics/face/aidl/default/Face.cpp b/biometrics/face/aidl/default/Face.cpp
index 929c7e7..d3883d6 100644
--- a/biometrics/face/aidl/default/Face.cpp
+++ b/biometrics/face/aidl/default/Face.cpp
@@ -20,7 +20,7 @@
namespace aidl::android::hardware::biometrics::face {
const int kSensorId = 0;
-const common::SensorStrength kSensorStrength = common::SensorStrength::WEAK;
+const common::SensorStrength kSensorStrength = common::SensorStrength::STRONG;
const int kMaxEnrollmentsPerUser = 5;
const FaceSensorType kSensorType = FaceSensorType::RGB;
const bool kHalControlsPreview = true;
diff --git a/biometrics/face/aidl/default/Session.cpp b/biometrics/face/aidl/default/Session.cpp
index 2b1d4a7..c5d7d23 100644
--- a/biometrics/face/aidl/default/Session.cpp
+++ b/biometrics/face/aidl/default/Session.cpp
@@ -21,17 +21,35 @@
namespace aidl::android::hardware::biometrics::face {
class CancellationSignal : public common::BnCancellationSignal {
+ private:
+ std::shared_ptr<ISessionCallback> cb_;
public:
- ndk::ScopedAStatus cancel() override { return ndk::ScopedAStatus::ok(); }
+ explicit CancellationSignal(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
+
+ ndk::ScopedAStatus cancel() override {
+ cb_->onError(Error::CANCELED, 0 /* vendorCode */);
+ cb_->onStateChanged(0, SessionState::IDLING);
+ return ndk::ScopedAStatus::ok();
+ }
};
Session::Session(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
ndk::ScopedAStatus Session::generateChallenge(int32_t /*cookie*/, int32_t /*timeoutSec*/) {
+ if (cb_) {
+ cb_->onStateChanged(0, SessionState::GENERATING_CHALLENGE);
+ cb_->onChallengeGenerated(0);
+ cb_->onStateChanged(0, SessionState::IDLING);
+ }
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::revokeChallenge(int32_t /*cookie*/, int64_t /*challenge*/) {
+ndk::ScopedAStatus Session::revokeChallenge(int32_t /*cookie*/, int64_t challenge) {
+ if (cb_) {
+ cb_->onStateChanged(0, SessionState::REVOKING_CHALLENGE);
+ cb_->onChallengeRevoked(challenge);
+ cb_->onStateChanged(0, SessionState::IDLING);
+ }
return ndk::ScopedAStatus::ok();
}
@@ -47,7 +65,7 @@
if (cb_) {
cb_->onStateChanged(0, SessionState::AUTHENTICATING);
}
- *return_val = SharedRefBase::make<CancellationSignal>();
+ *return_val = SharedRefBase::make<CancellationSignal>(cb_);
return ndk::ScopedAStatus::ok();
}
@@ -57,15 +75,30 @@
}
ndk::ScopedAStatus Session::enumerateEnrollments(int32_t /*cookie*/) {
+ if (cb_) {
+ cb_->onStateChanged(0, SessionState::ENUMERATING_ENROLLMENTS);
+ cb_->onEnrollmentsEnumerated(std::vector<int32_t>());
+ cb_->onStateChanged(0, SessionState::IDLING);
+ }
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::removeEnrollments(int32_t /*cookie*/,
const std::vector<int32_t>& /*enrollmentIds*/) {
+ if (cb_) {
+ cb_->onStateChanged(0, SessionState::REMOVING_ENROLLMENTS);
+ cb_->onEnrollmentsRemoved(std::vector<int32_t>());
+ cb_->onStateChanged(0, SessionState::IDLING);
+ }
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::getAuthenticatorId(int32_t /*cookie*/) {
+ if (cb_) {
+ cb_->onStateChanged(0, SessionState::GETTING_AUTHENTICATOR_ID);
+ cb_->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
+ cb_->onStateChanged(0, SessionState::IDLING);
+ }
return ndk::ScopedAStatus::ok();
}
@@ -75,6 +108,11 @@
ndk::ScopedAStatus Session::resetLockout(int32_t /*cookie*/,
const keymaster::HardwareAuthToken& /*hat*/) {
+ if (cb_) {
+ cb_->onStateChanged(0, SessionState::RESETTING_LOCKOUT);
+ cb_->onLockoutCleared();
+ cb_->onStateChanged(0, SessionState::IDLING);
+ }
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 18ce0c8..6eb35d9 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -19,7 +19,7 @@
namespace aidl::android::hardware::biometrics::fingerprint {
-const int kSensorId = 0;
+const int kSensorId = 1;
const common::SensorStrength kSensorStrength = common::SensorStrength::STRONG;
const int kMaxEnrollmentsPerUser = 5;
const FingerprintSensorType kSensorType = FingerprintSensorType::REAR;
diff --git a/bluetooth/1.0/default/h4_protocol.cc b/bluetooth/1.0/default/h4_protocol.cc
index 8c24f76..43abbe4 100644
--- a/bluetooth/1.0/default/h4_protocol.cc
+++ b/bluetooth/1.0/default/h4_protocol.cc
@@ -90,6 +90,7 @@
hci_packet_type_ = static_cast<HciPacketType>(buffer[0]);
if (hci_packet_type_ != HCI_PACKET_TYPE_ACL_DATA &&
hci_packet_type_ != HCI_PACKET_TYPE_SCO_DATA &&
+ hci_packet_type_ != HCI_PACKET_TYPE_ISO_DATA &&
hci_packet_type_ != HCI_PACKET_TYPE_EVENT) {
LOG_ALWAYS_FATAL("%s: Unimplemented packet type %d", __func__,
static_cast<int>(hci_packet_type_));
diff --git a/bluetooth/1.0/default/hci_internals.h b/bluetooth/1.0/default/hci_internals.h
index 24e944f..6f7ff90 100644
--- a/bluetooth/1.0/default/hci_internals.h
+++ b/bluetooth/1.0/default/hci_internals.h
@@ -44,6 +44,10 @@
const size_t HCI_EVENT_PREAMBLE_SIZE = 2;
const size_t HCI_LENGTH_OFFSET_EVT = 1;
+// 2 bytes for handle and flags, 2 byte for data length (Volume 4, Part E, 5.4.5)
+const size_t HCI_ISO_PREAMBLE_SIZE = 4;
+const size_t HCI_LENGTH_OFFSET_ISO = 2;
+
const size_t HCI_PREAMBLE_SIZE_MAX = HCI_ACL_PREAMBLE_SIZE;
// Event codes (Volume 2, Part E, 7.7.14)
diff --git a/bluetooth/1.0/default/hci_packetizer.cc b/bluetooth/1.0/default/hci_packetizer.cc
index 7cb3a11..78ce61d 100644
--- a/bluetooth/1.0/default/hci_packetizer.cc
+++ b/bluetooth/1.0/default/hci_packetizer.cc
@@ -26,17 +26,27 @@
namespace {
-const size_t preamble_size_for_type[] = {
- 0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE,
- HCI_EVENT_PREAMBLE_SIZE};
-const size_t packet_length_offset_for_type[] = {
- 0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO,
- HCI_LENGTH_OFFSET_EVT};
+const size_t preamble_size_for_type[] = {0,
+ HCI_COMMAND_PREAMBLE_SIZE,
+ HCI_ACL_PREAMBLE_SIZE,
+ HCI_SCO_PREAMBLE_SIZE,
+ HCI_EVENT_PREAMBLE_SIZE,
+ HCI_ISO_PREAMBLE_SIZE};
+const size_t packet_length_offset_for_type[] = {0,
+ HCI_LENGTH_OFFSET_CMD,
+ HCI_LENGTH_OFFSET_ACL,
+ HCI_LENGTH_OFFSET_SCO,
+ HCI_LENGTH_OFFSET_EVT,
+ HCI_LENGTH_OFFSET_ISO};
size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) {
size_t offset = packet_length_offset_for_type[type];
- if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset];
- return (((preamble[offset + 1]) << 8) | preamble[offset]);
+ if (type == HCI_PACKET_TYPE_ACL_DATA) {
+ return (((preamble[offset + 1]) << 8) | preamble[offset]);
+ } else if (type == HCI_PACKET_TYPE_ISO_DATA) {
+ return ((((preamble[offset + 1]) & 0x3f) << 8) | preamble[offset]);
+ }
+ return preamble[offset];
}
} // namespace
diff --git a/bluetooth/1.0/default/test/h4_protocol_unittest.cc b/bluetooth/1.0/default/test/h4_protocol_unittest.cc
index 283243d..174861c 100644
--- a/bluetooth/1.0/default/test/h4_protocol_unittest.cc
+++ b/bluetooth/1.0/default/test/h4_protocol_unittest.cc
@@ -190,8 +190,10 @@
void WriteAndExpectInboundIsoData(char* payload) {
// h4 type[1] + handle[2] + size[1]
- char preamble[4] = {HCI_PACKET_TYPE_ISO_DATA, 20, 17, 0};
- preamble[3] = strlen(payload) & 0xFF;
+ char preamble[5] = {HCI_PACKET_TYPE_ISO_DATA, 19, 92, 0, 0};
+ int length = strlen(payload);
+ preamble[3] = length & 0xFF;
+ preamble[4] = (length >> 8) & 0x3F;
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble)));
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
index 0f349a4..092038b 100644
--- a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
@@ -55,12 +55,17 @@
const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
AudioConfiguration audioConfig_2_1;
- audioConfig_2_1.codecConfig() = audioConfig.codecConfig();
- audioConfig_2_1.pcmConfig() = {
- .sampleRate = static_cast<SampleRate>(audioConfig.pcmConfig().sampleRate),
- .channelMode = audioConfig.pcmConfig().channelMode,
- .bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
- .dataIntervalUs = 0};
+ if (audioConfig.getDiscriminator() ==
+ V2_0::AudioConfiguration::hidl_discriminator::pcmConfig) {
+ audioConfig_2_1.pcmConfig() = {
+ .sampleRate =
+ static_cast<SampleRate>(audioConfig.pcmConfig().sampleRate),
+ .channelMode = audioConfig.pcmConfig().channelMode,
+ .bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
+ .dataIntervalUs = 0};
+ } else {
+ audioConfig_2_1.codecConfig() = audioConfig.codecConfig();
+ }
return startSession_2_1(hostIf, audioConfig_2_1, _hidl_cb);
}
diff --git a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
index c0ec907..37d1281 100644
--- a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
+++ b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
@@ -1043,6 +1043,7 @@
} else {
EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
EXPECT_FALSE(dataMQ.isHandleValid());
+ tempDataMQ.reset(nullptr);
}
};
android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
@@ -1064,6 +1065,8 @@
ASSERT_TRUE(hidl_retval.isOk());
if (is_codec_config_valid) {
EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+ } else {
+ EXPECT_TRUE(tempDataMQ == nullptr);
}
EXPECT_TRUE(audio_provider_2_1_->endSession().isOk());
} // uint32_t (data interval in microseconds)
@@ -1132,6 +1135,7 @@
} else {
EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
EXPECT_FALSE(dataMQ.isHandleValid());
+ tempDataMQ.reset(nullptr);
}
};
android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 9a5838b..74af9e3 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -430,6 +430,14 @@
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.radio.config</name>
+ <version>1.3</version>
+ <interface>
+ <name>IRadioConfig</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.renderscript</name>
<version>1.0</version>
<interface>
diff --git a/current.txt b/current.txt
index 7ef176b..7aa9593 100644
--- a/current.txt
+++ b/current.txt
@@ -769,7 +769,12 @@
# ABI preserving changes to HALs during Android S
1ca372cd67d197df099e87616a613ba6ede6552638a603e18f86c8834302c3d1 android.hardware.gnss@1.0::IGnssMeasurementCallback
6a271e493907e8ba20912e42771bd0d99ae45431a851d5675ef9496d02510a34 android.hardware.gnss@1.1::IGnssMeasurementCallback
+2c331a9605f3a08d9c1e0a36169ca57758bc43c11a78ef3f3730509885e52c15 android.hardware.graphics.composer@2.4::IComposerClient
3da3ce039247872d95c6bd48621dbfdfa1c2d2a91a90f257862f87ee2bc46300 android.hardware.health@2.1::types
+9679f27a42f75781c8993ef163ed92808a1928de186639834841d0b8e326e63d android.hardware.gatekeeper@1.0::IGatekeeper
+40456eb90ea88b62d18ad3fbf1da8917981cd55ac04ce69c8e058d49ff5beff4 android.hardware.keymaster@3.0::IKeymasterDevice
+6017b4f2481feb0fffceae81c62bc372c898998b2d8fe69fbd39859d3a315e5e android.hardware.keymaster@4.0::IKeymasterDevice
+dabe23dde7c9e3ad65c61def7392f186d7efe7f4216f9b6f9cf0863745b1a9f4 android.hardware.keymaster@4.1::IKeymasterDevice
cd84ab19c590e0e73dd2307b591a3093ee18147ef95e6d5418644463a6620076 android.hardware.neuralnetworks@1.2::IDevice
9625e85f56515ad2cf87b6a1847906db669f746ea4ab02cd3d4ca25abc9b0109 android.hardware.neuralnetworks@1.2::types
9e758e208d14f7256e0885d6d8ad0b61121b21d8c313864f981727ae55bffd16 android.hardware.neuralnetworks@1.3::types
diff --git a/gatekeeper/1.0/Android.bp b/gatekeeper/1.0/Android.bp
index 28fd5b6..f5cb8e4 100644
--- a/gatekeeper/1.0/Android.bp
+++ b/gatekeeper/1.0/Android.bp
@@ -10,5 +10,5 @@
interfaces: [
"android.hidl.base@1.0",
],
- gen_java: true,
+ gen_java: false,
}
diff --git a/gatekeeper/1.0/IGatekeeper.hal b/gatekeeper/1.0/IGatekeeper.hal
index 59dd7d1..84e8e06 100644
--- a/gatekeeper/1.0/IGatekeeper.hal
+++ b/gatekeeper/1.0/IGatekeeper.hal
@@ -15,6 +15,7 @@
*/
package android.hardware.gatekeeper@1.0;
+@SensitiveData
interface IGatekeeper {
/**
diff --git a/graphics/composer/2.1/utils/resources/Android.bp b/graphics/composer/2.1/utils/resources/Android.bp
index dc20eae..d77bff0 100644
--- a/graphics/composer/2.1/utils/resources/Android.bp
+++ b/graphics/composer/2.1/utils/resources/Android.bp
@@ -15,6 +15,7 @@
cc_library {
name: "android.hardware.graphics.composer@2.1-resources",
+ system_ext_specific: true,
defaults: ["hidl_defaults"],
vendor_available: true,
shared_libs: [
diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal
index 9e3cf0e..1a59bbd 100644
--- a/graphics/composer/2.4/IComposerClient.hal
+++ b/graphics/composer/2.4/IComposerClient.hal
@@ -34,7 +34,9 @@
/**
* The configuration group ID (as int32_t) this config is associated to.
* Switching between configurations within the same group may be done seamlessly
- * in some conditions via setActiveConfigWithConstraints.
+ * in some conditions via setActiveConfigWithConstraints. Configurations which
+ * share the same config group are similar in all attributes except for the
+ * vsync period.
*/
CONFIG_GROUP = 7,
};
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index 730b601..702334d 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -55,7 +55,7 @@
* This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
- * @return the unencrypted key-pair in PKCS#8 format.
+ * @return the private key, in DER format as specified in RFC 5915.
*/
byte[] createEphemeralKeyPair();
@@ -88,10 +88,10 @@
* The setRequestedNamespaces() and setVerificationToken() methods will be called before
* this method is called.
*
- * This method be called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(),
- * createAuthChallenge() and before startRetrieveEntry(). This method call is followed by
- * multiple calls of startRetrieveEntryValue(), retrieveEntryValue(), and finally
- * finishRetrieval().
+ * This method is called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(),
+ * createAuthChallenge() (note that those calls are optional) and before startRetrieveEntry().
+ * This method call is followed by multiple calls of startRetrieveEntryValue(),
+ * retrieveEntryValue(), and finally finishRetrieval().
*
* It is permissible to perform data retrievals multiple times using the same instance (e.g.
* startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(),
@@ -343,12 +343,13 @@
*
* - signature: must be set to ECDSA.
*
- * - subject: CN shall be set to "Android Identity Credential Authentication Key".
+ * - subject: CN shall be set to "Android Identity Credential Authentication Key". (fixed
+ * value: same on all certs)
*
- * - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
- * values returned in HardwareInformation.
+ * - issuer: CN shall be set to "Android Identity Credential Key". (fixed value:
+ * same on all certs)
*
- * - validity: should be from current time and one year in the future.
+ * - validity: should be from current time and one year in the future (365 days).
*
* - subjectPublicKeyInfo: must contain attested public key.
*
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index 297fd1d..c48cb66 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -37,12 +37,12 @@
*
* - signature: must be set to ECDSA.
*
- * - subject: CN shall be set to "Android Identity Credential Key".
+ * - subject: CN shall be set to "Android Identity Credential Key". (fixed value:
+ * same on all certs)
*
- * - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
- * values returned in HardwareInformation.
+ * - issuer: Same as the subject field of the batch attestation key.
*
- * - validity: should be from current time and expire at the same time as the
+ * - validity: Should be set to current time and expire at the same time as the
* attestation batch certificate used.
*
* - subjectPublicKeyInfo: must contain attested public key.
@@ -55,19 +55,14 @@
*
* - The attestationSecurityLevel field must be set to either Software (0),
* TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
- * implemented. Only the default AOSP implementation of this HAL may use
- * value 0 (additionally, this implementation must not be used on production
- * devices).
+ * implemented.
*
- * - The keymasterVersion field in the attestation extension must be set to (10*major + minor)
- * where major and minor are the Identity Credential interface major and minor versions.
- * Specifically for this version of the interface (1.0) this value is 10.
+ * - The keymasterVersion field in the attestation extension must be set to the.
+ * same value as used for Android Keystore keys.
*
* - The keymasterSecurityLevel field in the attestation extension must be set to
* either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
- * the Trusted Application backing the HAL implementation is implemented. Only
- * the default AOSP implementation of this HAL may use value 0 (additionally, this
- * implementation must not be used on production devices)
+ * the Trusted Application backing the HAL implementation is implemented.
*
* - The attestationChallenge field must be set to the passed-in challenge.
*
@@ -81,7 +76,8 @@
*
* - Tag::IDENTITY_CREDENTIAL_KEY which indicates that the key is an Identity
* Credential key (which can only sign/MAC very specific messages) and not an Android
- * Keystore key (which can be used to sign/MAC anything).
+ * Keystore key (which can be used to sign/MAC anything). This must not be set
+ * for test credentials.
*
* - Tag::PURPOSE must be set to SIGN
*
@@ -95,10 +91,13 @@
*
* - Tag::EC_CURVE must be set to P_256
*
- * Additional authorizations may be needed in the softwareEnforced and teeEnforced
- * fields - the above is not an exhaustive list. Specifically, authorizations containing
- * information about the root of trust, OS version, verified boot state, and so on should
- * be included.
+ * - Tag::ROOT_OF_TRUST must be set
+ *
+ * - Tag::OS_VERSION and Tag::OS_PATCHLEVEL must be set
+ *
+ * Additional authorizations may be appear in the softwareEnforced and teeEnforced
+ * fields. For example if the device has a boot or vendor partitions, then BOOT_PATCHLEVEL
+ * and VENDOR_PATCHLEVEL should be set.
*
* Since the chain is required to be generated using Keymaster Attestation, the returned
* certificate chain has the following properties:
@@ -112,8 +111,8 @@
* As with any user of attestation, the Issuing Authority (as a relying party) wishing
* to issue a credential to a device using these APIs, must carefully examine the
* returned certificate chain for all of the above (and more). In particular, the Issuing
- * Authority should check the root of trust, verified boot state, patch level,
- * application id, etc.
+ * Authority should check the root of trust (which include verified boot state), patch level,
+ * attestation application id, etc.
*
* This all depends on the needs of the Issuing Authority and the kind of credential but
* in general an Issuing Authority should never issue a credential to a device without
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
index 87d9a93..dfcd4f5 100644
--- a/identity/aidl/default/IdentityCredential.cpp
+++ b/identity/aidl/default/IdentityCredential.cpp
@@ -272,6 +272,7 @@
const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
const vector<uint8_t>& signingKeyBlob, const vector<uint8_t>& sessionTranscript,
const vector<uint8_t>& readerSignature, const vector<int32_t>& requestCounts) {
+ std::unique_ptr<cppbor::Item> sessionTranscriptItem;
if (sessionTranscript.size() > 0) {
auto [item, _, message] = cppbor::parse(sessionTranscript);
if (item == nullptr) {
@@ -279,7 +280,7 @@
IIdentityCredentialStore::STATUS_INVALID_DATA,
"SessionTranscript contains invalid CBOR"));
}
- sessionTranscriptItem_ = std::move(item);
+ sessionTranscriptItem = std::move(item);
}
if (numStartRetrievalCalls_ > 0) {
if (sessionTranscript_ != sessionTranscript) {
@@ -319,7 +320,7 @@
vector<uint8_t> encodedReaderAuthentication =
cppbor::Array()
.add("ReaderAuthentication")
- .add(sessionTranscriptItem_->clone())
+ .add(std::move(sessionTranscriptItem))
.add(cppbor::Semantic(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
@@ -776,13 +777,6 @@
optional<vector<uint8_t>> mac;
if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
readerPublicKey_.size() > 0) {
- cppbor::Array array;
- array.add("DeviceAuthentication");
- array.add(sessionTranscriptItem_->clone());
- array.add(docType_);
- array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
- vector<uint8_t> deviceAuthenticationBytes = cppbor::Semantic(24, array.encode()).encode();
-
vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
optional<vector<uint8_t>> signingKey =
support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
@@ -792,31 +786,15 @@
"Error decrypting signingKeyBlob"));
}
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(readerPublicKey_, signingKey.value());
- if (!sharedSecret) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH"));
- }
-
- // Mix-in SessionTranscriptBytes
vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
- vector<uint8_t> sharedSecretWithSessionTranscriptBytes = sharedSecret.value();
- std::copy(sessionTranscriptBytes.begin(), sessionTranscriptBytes.end(),
- std::back_inserter(sharedSecretWithSessionTranscriptBytes));
-
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey =
- support::hkdf(sharedSecretWithSessionTranscriptBytes, salt, info, 32);
- if (!derivedKey) {
+ optional<vector<uint8_t>> eMacKey =
+ support::calcEMacKey(signingKey.value(), readerPublicKey_, sessionTranscriptBytes);
+ if (!eMacKey) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED,
- "Error deriving key from shared secret"));
+ IIdentityCredentialStore::STATUS_FAILED, "Error calculating EMacKey"));
}
-
- mac = support::coseMac0(derivedKey.value(), {}, // payload
- deviceAuthenticationBytes); // detached content
+ mac = support::calcMac(sessionTranscript_, docType_, encodedDeviceNameSpaces,
+ eMacKey.value());
if (!mac) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
@@ -830,9 +808,9 @@
ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
- string serialDecimal = "0"; // TODO: set serial to something unique
- string issuer = "Android Open Source Project";
- string subject = "Android IdentityCredential Reference Implementation";
+ 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;
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h
index a82531d..a8a6409 100644
--- a/identity/aidl/default/IdentityCredential.h
+++ b/identity/aidl/default/IdentityCredential.h
@@ -103,7 +103,6 @@
map<int32_t, int> profileIdToAccessCheckResult_;
vector<uint8_t> signingKeyBlob_;
vector<uint8_t> sessionTranscript_;
- std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
vector<uint8_t> itemsRequest_;
vector<int32_t> requestCountsRemaining_;
map<string, set<string>> requestedNameSpacesAndNames_;
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
index fea289b..141b4de 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -74,7 +74,7 @@
vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
- support::createEcKeyPairAndAttestation(challenge, appId);
+ support::createEcKeyPairAndAttestation(challenge, appId, testCredential_);
if (!keyAttestationPair) {
LOG(ERROR) << "Error creating credentialKey and attestation";
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index c1f44e7..03966de 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -9,7 +9,6 @@
"VtsIWritableIdentityCredentialTests.cpp",
"VtsIdentityTestUtils.cpp",
"VtsAttestationTests.cpp",
- "VtsAttestationParserSupport.cpp",
"UserAuthTests.cpp",
"ReaderAuthTests.cpp",
],
@@ -20,13 +19,14 @@
static_libs: [
"libcppbor",
"libkeymaster_portable",
- "libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.keymaster@4.0",
"android.hardware.identity-support-lib",
"android.hardware.identity-cpp",
"android.hardware.keymaster-cpp",
"android.hardware.keymaster-ndk_platform",
+ "libkeymaster4support",
+ "libkeymaster4_1support",
],
test_suites: [
"general-tests",
diff --git a/identity/aidl/vts/VtsAttestationParserSupport.cpp b/identity/aidl/vts/VtsAttestationParserSupport.cpp
deleted file mode 100644
index 71fe733..0000000
--- a/identity/aidl/vts/VtsAttestationParserSupport.cpp
+++ /dev/null
@@ -1,187 +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.
- */
-
-#include "VtsAttestationParserSupport.h"
-
-#include <aidl/Gtest.h>
-#include <map>
-
-namespace android::hardware::identity::test_utils {
-
-using std::endl;
-using std::map;
-using std::optional;
-using std::string;
-using std::vector;
-
-using ::android::sp;
-using ::android::String16;
-using ::android::binder::Status;
-
-using ::keymaster::ASN1_OBJECT_Ptr;
-using ::keymaster::AuthorizationSet;
-using ::keymaster::EVP_PKEY_Ptr;
-using ::keymaster::kAttestionRecordOid;
-using ::keymaster::TAG_ATTESTATION_APPLICATION_ID;
-using ::keymaster::TAG_IDENTITY_CREDENTIAL_KEY;
-using ::keymaster::TAG_INCLUDE_UNIQUE_ID;
-using ::keymaster::TypedTag;
-using ::keymaster::X509_Ptr;
-
-using support::certificateChainSplit;
-
-optional<keymaster_cert_chain_t> AttestationCertificateParser::certificateChainToKeymasterChain(
- const vector<Certificate>& certificates) {
- if (certificates.size() <= 0) {
- return {};
- }
-
- keymaster_cert_chain_t kCert;
- kCert.entry_count = certificates.size();
- kCert.entries = (keymaster_blob_t*)malloc(sizeof(keymaster_blob_t) * kCert.entry_count);
-
- int index = 0;
- for (const auto& c : certificates) {
- kCert.entries[index].data_length = c.encodedCertificate.size();
- uint8_t* data = (uint8_t*)malloc(c.encodedCertificate.size());
-
- memcpy(data, c.encodedCertificate.data(), c.encodedCertificate.size());
- kCert.entries[index].data = (const uint8_t*)data;
- index++;
- }
-
- return kCert;
-}
-
-bool AttestationCertificateParser::parse() {
- optional<keymaster_cert_chain_t> cert_chain = certificateChainToKeymasterChain(origCertChain_);
- if (!cert_chain) {
- return false;
- }
-
- if (cert_chain.value().entry_count < 3) {
- return false;
- }
-
- if (!verifyChain(cert_chain.value())) {
- return false;
- }
-
- if (!verifyAttestationRecord(cert_chain.value().entries[0])) {
- return false;
- }
-
- keymaster_free_cert_chain(&cert_chain.value());
- return true;
-}
-
-ASN1_OCTET_STRING* AttestationCertificateParser::getAttestationRecord(X509* certificate) {
- ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1));
- if (!oid.get()) return nullptr;
-
- int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1);
- if (location == -1) return nullptr;
-
- X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
- if (!attest_rec_ext) return nullptr;
-
- ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
- return attest_rec;
-}
-
-X509* AttestationCertificateParser::parseCertBlob(const keymaster_blob_t& blob) {
- const uint8_t* p = blob.data;
- return d2i_X509(nullptr, &p, blob.data_length);
-}
-
-bool AttestationCertificateParser::verifyAttestationRecord(
- const keymaster_blob_t& attestation_cert) {
- X509_Ptr cert(parseCertBlob(attestation_cert));
- if (!cert.get()) {
- return false;
- }
-
- ASN1_OCTET_STRING* attest_rec = getAttestationRecord(cert.get());
- if (!attest_rec) {
- return false;
- }
-
- keymaster_blob_t att_unique_id = {};
- keymaster_blob_t att_challenge;
- keymaster_error_t ret = parse_attestation_record(
- attest_rec->data, attest_rec->length, &att_attestation_version_,
- &att_attestation_security_level_, &att_keymaster_version_,
- &att_keymaster_security_level_, &att_challenge, &att_sw_enforced_, &att_hw_enforced_,
- &att_unique_id);
- if (ret) {
- return false;
- }
-
- att_challenge_.assign(att_challenge.data, att_challenge.data + att_challenge.data_length);
- return true;
-}
-
-uint32_t AttestationCertificateParser::getKeymasterVersion() {
- return att_keymaster_version_;
-}
-
-uint32_t AttestationCertificateParser::getAttestationVersion() {
- return att_attestation_version_;
-}
-
-vector<uint8_t> AttestationCertificateParser::getAttestationChallenge() {
- return att_challenge_;
-}
-
-keymaster_security_level_t AttestationCertificateParser::getKeymasterSecurityLevel() {
- return att_keymaster_security_level_;
-}
-
-keymaster_security_level_t AttestationCertificateParser::getAttestationSecurityLevel() {
- return att_attestation_security_level_;
-}
-
-// Verify the Attestation certificates are correctly chained.
-bool AttestationCertificateParser::verifyChain(const keymaster_cert_chain_t& chain) {
- for (size_t i = 0; i < chain.entry_count - 1; ++i) {
- keymaster_blob_t& key_cert_blob = chain.entries[i];
- keymaster_blob_t& signing_cert_blob = chain.entries[i + 1];
-
- X509_Ptr key_cert(parseCertBlob(key_cert_blob));
- X509_Ptr signing_cert(parseCertBlob(signing_cert_blob));
- if (!key_cert.get() || !signing_cert.get()) {
- return false;
- }
-
- EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
- if (!signing_pubkey.get()) return false;
-
- if (X509_verify(key_cert.get(), signing_pubkey.get()) != 1) {
- return false;
- }
-
- if (i + 1 == chain.entry_count - 1) {
- // Last entry is self-signed.
- if (X509_verify(signing_cert.get(), signing_pubkey.get()) != 1) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-} // namespace android::hardware::identity::test_utils
diff --git a/identity/aidl/vts/VtsAttestationParserSupport.h b/identity/aidl/vts/VtsAttestationParserSupport.h
deleted file mode 100644
index 7c7e1b6..0000000
--- a/identity/aidl/vts/VtsAttestationParserSupport.h
+++ /dev/null
@@ -1,122 +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 VTS_ATTESTATION_PARSER_SUPPORT_H
-#define VTS_ATTESTATION_PARSER_SUPPORT_H
-
-//#include <aidl/Gtest.h>
-#include <android/hardware/identity/IIdentityCredentialStore.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-#include <android/hardware/keymaster/4.0/types.h>
-#include <hardware/keymaster_defs.h>
-#include <keymaster/android_keymaster_utils.h>
-#include <keymaster/authorization_set.h>
-#include <keymaster/contexts/pure_soft_keymaster_context.h>
-#include <keymaster/contexts/soft_attestation_cert.h>
-#include <keymaster/keymaster_tags.h>
-#include <keymaster/km_openssl/attestation_utils.h>
-#include <vector>
-
-namespace android::hardware::identity::test_utils {
-
-using ::std::optional;
-using ::std::string;
-using ::std::vector;
-
-using ::keymaster::AuthorizationSet;
-using ::keymaster::TypedTag;
-
-class AttestationCertificateParser {
- public:
- AttestationCertificateParser(const vector<Certificate>& certChain)
- : origCertChain_(certChain) {}
-
- bool parse();
-
- uint32_t getKeymasterVersion();
- uint32_t getAttestationVersion();
- vector<uint8_t> getAttestationChallenge();
- keymaster_security_level_t getKeymasterSecurityLevel();
- keymaster_security_level_t getAttestationSecurityLevel();
-
- template <keymaster_tag_t Tag>
- bool getSwEnforcedBool(TypedTag<KM_BOOL, Tag> tag) {
- if (att_sw_enforced_.GetTagValue(tag)) {
- return true;
- }
-
- return false;
- }
-
- template <keymaster_tag_t Tag>
- bool getHwEnforcedBool(TypedTag<KM_BOOL, Tag> tag) {
- if (att_hw_enforced_.GetTagValue(tag)) {
- return true;
- }
-
- return false;
- }
-
- template <keymaster_tag_t Tag>
- optional<vector<uint8_t>> getHwEnforcedBlob(TypedTag<KM_BYTES, Tag> tag) {
- keymaster_blob_t blob;
- if (att_hw_enforced_.GetTagValue(tag, &blob)) {
- return {};
- }
-
- vector<uint8_t> ret(blob.data, blob.data + blob.data_length);
- return ret;
- }
-
- template <keymaster_tag_t Tag>
- optional<vector<uint8_t>> getSwEnforcedBlob(TypedTag<KM_BYTES, Tag> tag) {
- keymaster_blob_t blob;
- if (!att_sw_enforced_.GetTagValue(tag, &blob)) {
- return {};
- }
-
- vector<uint8_t> ret(blob.data, blob.data + blob.data_length);
- return ret;
- }
-
- private:
- // Helper functions.
- bool verifyChain(const keymaster_cert_chain_t& chain);
-
- ASN1_OCTET_STRING* getAttestationRecord(X509* certificate);
-
- X509* parseCertBlob(const keymaster_blob_t& blob);
-
- bool verifyAttestationRecord(const keymaster_blob_t& attestation_cert);
-
- optional<keymaster_cert_chain_t> certificateChainToKeymasterChain(
- const vector<Certificate>& certificates);
-
- // Private variables.
- vector<Certificate> origCertChain_;
- AuthorizationSet att_sw_enforced_;
- AuthorizationSet att_hw_enforced_;
- uint32_t att_attestation_version_;
- uint32_t att_keymaster_version_;
- keymaster_security_level_t att_attestation_security_level_;
- keymaster_security_level_t att_keymaster_security_level_;
- vector<uint8_t> att_challenge_;
-};
-
-} // namespace android::hardware::identity::test_utils
-
-#endif // VTS_ATTESTATION_PARSER_SUPPORT_H
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index 673d08b..5529853 100644
--- a/identity/aidl/vts/VtsAttestationTests.cpp
+++ b/identity/aidl/vts/VtsAttestationTests.cpp
@@ -29,7 +29,6 @@
#include <future>
#include <map>
-#include "VtsAttestationParserSupport.h"
#include "VtsIdentityTestUtils.h"
namespace android::hardware::identity {
@@ -44,7 +43,6 @@
using ::android::String16;
using ::android::binder::Status;
-using test_utils::AttestationCertificateParser;
using test_utils::setupWritableCredential;
using test_utils::validateAttestationCertificate;
@@ -61,38 +59,12 @@
sp<IIdentityCredentialStore> credentialStore_;
};
-TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeEmptyId) {
- Status result;
-
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
- sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
-
- string challenge = "NotSoRandomChallenge";
- vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
- vector<Certificate> attestationCertificate;
- vector<uint8_t> attestationApplicationId = {};
-
- result = writableCredential->getAttestationCertificate(
- attestationApplicationId, attestationChallenge, &attestationCertificate);
-
- ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
- << endl;
-
- EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
- attestationApplicationId, hwInfo));
-}
-
TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeNonemptyId) {
Status result;
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1";
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
@@ -106,18 +78,16 @@
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
- EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
- attestationApplicationId, hwInfo));
+ validateAttestationCertificate(attestationCertificate, attestationChallenge,
+ attestationApplicationId, false);
}
TEST_P(VtsAttestationTests, verifyAttestationWithVeryShortChallengeAndId) {
Status result;
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "c";
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
@@ -131,8 +101,8 @@
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
- EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
- attestationApplicationId, hwInfo));
+ validateAttestationCertificate(attestationCertificate, attestationChallenge,
+ attestationApplicationId, false);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsAttestationTests);
diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
index 500b79f..cdecb97 100644
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
@@ -174,16 +174,17 @@
string cborPretty;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ true /* testCredential */));
string challenge = "attestationChallenge";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {1} /* atteestationApplicationId */);
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
- EXPECT_TRUE(validateAttestationCertificate(attData.attestationCertificate,
- attData.attestationChallenge,
- attData.attestationApplicationId, hwInfo));
+ validateAttestationCertificate(attData.attestationCertificate, attData.attestationChallenge,
+ attData.attestationApplicationId, true);
// This is kinda of a hack but we need to give the size of
// ProofOfProvisioning that we'll expect to receive.
@@ -368,6 +369,7 @@
optional<vector<uint8_t>> signingPubKey =
support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
EXPECT_TRUE(signingPubKey);
+ test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
// Since we're using a test-credential we know storageKey meaning we can get the
// private key. Do this, derive the public key from it, and check this matches what
@@ -418,9 +420,9 @@
}
vector<uint8_t> mac;
- vector<uint8_t> deviceNameSpacesBytes;
- ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ vector<uint8_t> deviceNameSpacesEncoded;
+ ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ(
"{\n"
" 'PersonalData' : {\n"
@@ -435,37 +437,19 @@
" },\n"
"}",
cborPretty);
- // The data that is MACed is ["DeviceAuthentication", sessionTranscript, docType,
- // deviceNameSpacesBytes] so build up that structure
- cppbor::Array deviceAuthentication;
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
string docType = "org.iso.18013-5.2019.mdl";
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- vector<uint8_t> deviceAuthenticationBytes =
- cppbor::Semantic(24, deviceAuthentication.encode()).encode();
- // Derive the key used for MACing.
optional<vector<uint8_t>> readerEphemeralPrivateKey =
support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(signingPubKey.value(), readerEphemeralPrivateKey.value());
- ASSERT_TRUE(sharedSecret);
- // Mix-in SessionTranscriptBytes
- vector<uint8_t> sessionTranscriptBytes =
- cppbor::Semantic(24, sessionTranscript.encode()).encode();
- vector<uint8_t> sharedSecretWithSessionTranscriptBytes = sharedSecret.value();
- std::copy(sessionTranscriptBytes.begin(), sessionTranscriptBytes.end(),
- std::back_inserter(sharedSecretWithSessionTranscriptBytes));
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey =
- support::hkdf(sharedSecretWithSessionTranscriptBytes, salt, info, 32);
- ASSERT_TRUE(derivedKey);
+ optional<vector<uint8_t>> eMacKey = support::calcEMacKey(
+ readerEphemeralPrivateKey.value(), // Private Key
+ signingPubKey.value(), // Public Key
+ cppbor::Semantic(24, sessionTranscript.encode()).encode()); // SessionTranscriptBytes
optional<vector<uint8_t>> calculatedMac =
- support::coseMac0(derivedKey.value(), {}, // payload
- deviceAuthenticationBytes); // detached content
+ support::calcMac(sessionTranscript.encode(), // SessionTranscript
+ docType, // DocType
+ deviceNameSpacesEncoded, // DeviceNamespaces
+ eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
@@ -480,18 +464,14 @@
signingKeyBlob, sessionTranscriptEncoded, {}, // readerSignature,
testEntriesEntryCounts)
.isOk());
- ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ("{}", cborPretty);
// Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
- deviceAuthentication = cppbor::Array();
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- deviceAuthenticationBytes = cppbor::Semantic(24, deviceAuthentication.encode()).encode();
- calculatedMac = support::coseMac0(derivedKey.value(), {}, // payload
- deviceAuthenticationBytes); // detached content
+ calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
+ docType, // DocType
+ deviceNameSpacesEncoded, // DeviceNamespaces
+ eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
@@ -506,18 +486,14 @@
signingKeyBlob, sessionTranscriptEncoded, {}, // readerSignature,
testEntriesEntryCounts)
.isOk());
- ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ("{}", cborPretty);
// Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
- deviceAuthentication = cppbor::Array();
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- deviceAuthenticationBytes = cppbor::Semantic(24, deviceAuthentication.encode()).encode();
- calculatedMac = support::coseMac0(derivedKey.value(), {}, // payload
- deviceAuthenticationBytes); // detached content
+ calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
+ docType, // DocType
+ deviceNameSpacesEncoded, // DeviceNamespaces
+ eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
}
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 8c35952..56e17ba 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -61,7 +61,8 @@
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
vector<uint8_t> attestationChallenge;
vector<Certificate> attestationCertificate;
@@ -82,12 +83,13 @@
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1";
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
vector<Certificate> attestationCertificate;
- vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationApplicationId = {1};
result = writableCredential->getAttestationCertificate(
attestationApplicationId, attestationChallenge, &attestationCertificate);
@@ -95,27 +97,27 @@
EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
- EXPECT_TRUE(test_utils::validateAttestationCertificate(
- attestationCertificate, attestationChallenge, attestationApplicationId, hwInfo));
+ test_utils::validateAttestationCertificate(attestationCertificate, attestationChallenge,
+ attestationApplicationId, false);
}
TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) {
Status result;
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge1";
- test_utils::AttestationData attData(writableCredential, challenge, {});
- ASSERT_TRUE(test_utils::validateAttestationCertificate(
- attData.attestationCertificate, attData.attestationChallenge,
- attData.attestationApplicationId, hwInfo));
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {1} /* atteestationApplicationId */);
+ test_utils::validateAttestationCertificate(attData.attestationCertificate,
+ attData.attestationChallenge,
+ attData.attestationApplicationId, false);
string challenge2 = "NotSoRandomChallenge2";
- test_utils::AttestationData attData2(writableCredential, challenge2, {});
+ test_utils::AttestationData attData2(writableCredential, challenge2,
+ {} /* atteestationApplicationId */);
EXPECT_FALSE(attData2.result.isOk()) << attData2.result.exceptionCode() << "; "
<< attData2.result.exceptionMessage() << endl;
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, attData2.result.exceptionCode());
@@ -125,7 +127,8 @@
TEST_P(IdentityCredentialTests, verifyStartPersonalization) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// First call should go through
const vector<int32_t> entryCounts = {2, 4};
@@ -147,7 +150,8 @@
TEST_P(IdentityCredentialTests, verifyStartPersonalizationMin) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// Verify minimal number of profile count and entry count
const vector<int32_t> entryCounts = {1, 1};
@@ -160,7 +164,8 @@
TEST_P(IdentityCredentialTests, verifyStartPersonalizationOne) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// Verify minimal number of profile count and entry count
const vector<int32_t> entryCounts = {1};
@@ -173,7 +178,8 @@
TEST_P(IdentityCredentialTests, verifyStartPersonalizationLarge) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// Verify set a large number of profile count and entry count is ok
const vector<int32_t> entryCounts = {3000};
@@ -186,7 +192,8 @@
TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// Enter mismatched entry and profile numbers
const vector<int32_t> entryCounts = {5, 6};
@@ -224,7 +231,8 @@
TEST_P(IdentityCredentialTests, verifyDuplicateProfileId) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
const vector<int32_t> entryCounts = {3, 6};
writableCredential->setExpectedProofOfProvisioningSize(123456);
@@ -283,10 +291,12 @@
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge1";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {} /* atteestationApplicationId */);
EXPECT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
@@ -294,7 +304,7 @@
ASSERT_TRUE(readerCertificate1);
const vector<int32_t> entryCounts = {1u};
- size_t expectedPoPSize = 186 + readerCertificate1.value().size();
+ size_t expectedPoPSize = 185 + readerCertificate1.value().size();
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize);
result = writableCredential->startPersonalization(1, entryCounts);
@@ -308,7 +318,7 @@
ASSERT_TRUE(secureProfiles);
const vector<test_utils::TestEntryData> testEntries1 = {
- {"Name Space", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+ {"Name Space", "Last name", string("Turing"), vector<int32_t>{1}},
};
map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
@@ -347,11 +357,11 @@
" {\n"
" 'name' : 'Last name',\n"
" 'value' : 'Turing',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
+ " 'accessControlProfiles' : [1, ],\n"
" },\n"
" ],\n"
" },\n"
- " true,\n"
+ " false,\n"
"]",
cborPretty);
@@ -370,10 +380,12 @@
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {} /* atteestationApplicationId */);
EXPECT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
@@ -510,7 +522,7 @@
" },\n"
" ],\n"
" },\n"
- " true,\n"
+ " false,\n"
"]",
cborPretty);
@@ -529,10 +541,12 @@
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {} /* atteestationApplicationId */);
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
@@ -591,10 +605,12 @@
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {} /* atteestationApplicationId */);
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
@@ -667,7 +683,8 @@
TEST_P(IdentityCredentialTests, verifyAccessControlProfileIdOutOfRange) {
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
const vector<int32_t> entryCounts = {1};
writableCredential->setExpectedProofOfProvisioningSize(123456);
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp
index b6ed80f..3b10651 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ b/identity/aidl/vts/VtsIdentityTestUtils.cpp
@@ -14,13 +14,17 @@
* limitations under the License.
*/
+#define LOG_TAG "VtsIdentityTestUtils"
+
#include "VtsIdentityTestUtils.h"
#include <aidl/Gtest.h>
+#include <android-base/logging.h>
+#include <keymaster/km_openssl/openssl_utils.h>
+#include <keymasterV4_1/attestation_record.h>
+#include <charconv>
#include <map>
-#include "VtsAttestationParserSupport.h"
-
namespace android::hardware::identity::test_utils {
using std::endl;
@@ -32,15 +36,15 @@
using ::android::sp;
using ::android::String16;
using ::android::binder::Status;
+using ::keymaster::X509_Ptr;
bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
- sp<IIdentityCredentialStore>& credentialStore) {
+ sp<IIdentityCredentialStore>& credentialStore, bool testCredential) {
if (credentialStore == nullptr) {
return false;
}
string docType = "org.iso.18013-5.2019.mdl";
- bool testCredential = true;
Status result = credentialStore->createCredential(docType, testCredential, &writableCredential);
if (result.isOk() && writableCredential != nullptr) {
@@ -178,63 +182,269 @@
}
}
-bool validateAttestationCertificate(const vector<Certificate>& inputCertificates,
- const vector<uint8_t>& expectedChallenge,
- const vector<uint8_t>& expectedAppId,
- const HardwareInformation& hwInfo) {
- AttestationCertificateParser certParser_(inputCertificates);
- bool ret = certParser_.parse();
- EXPECT_TRUE(ret);
- if (!ret) {
+string x509NameToRfc2253String(X509_NAME* name) {
+ char* buf;
+ size_t bufSize;
+ BIO* bio;
+
+ bio = BIO_new(BIO_s_mem());
+ X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
+ bufSize = BIO_get_mem_data(bio, &buf);
+ string ret = string(buf, bufSize);
+ BIO_free(bio);
+
+ return ret;
+}
+
+int parseDigits(const char** s, int numDigits) {
+ int result;
+ auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
+ if (ec != std::errc()) {
+ LOG(ERROR) << "Error parsing " << numDigits << " digits "
+ << " from " << s;
+ return 0;
+ }
+ *s += numDigits;
+ return result;
+}
+
+bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
+ struct tm tm;
+
+ memset(&tm, '\0', sizeof(tm));
+ const char* timeStr = (const char*)asn1Time->data;
+ const char* s = timeStr;
+ if (asn1Time->type == V_ASN1_UTCTIME) {
+ tm.tm_year = parseDigits(&s, 2);
+ if (tm.tm_year < 70) {
+ tm.tm_year += 100;
+ }
+ } else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
+ tm.tm_year = parseDigits(&s, 4) - 1900;
+ tm.tm_year -= 1900;
+ } else {
+ LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
+ return false;
+ }
+ tm.tm_mon = parseDigits(&s, 2) - 1;
+ tm.tm_mday = parseDigits(&s, 2);
+ tm.tm_hour = parseDigits(&s, 2);
+ tm.tm_min = parseDigits(&s, 2);
+ tm.tm_sec = parseDigits(&s, 2);
+ // This may need to be updated if someone create certificates using +/- instead of Z.
+ //
+ if (*s != 'Z') {
+ LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
return false;
}
- // As per the IC HAL, the version of the Identity
- // Credential HAL is 1.0 - and this is encoded as major*10 + minor. This field is used by
- // Keymaster which is known to report integers less than or equal to 4 (for KM up to 4.0)
- // and integers greater or equal than 41 (for KM starting with 4.1).
- //
- // Since we won't get to version 4.0 of the IC HAL for a while, let's also check that a KM
- // version isn't errornously returned.
- EXPECT_LE(10, certParser_.getKeymasterVersion());
- EXPECT_GT(40, certParser_.getKeymasterVersion());
- EXPECT_LE(3, certParser_.getAttestationVersion());
-
- // Verify the app id matches to whatever we set it to be.
- optional<vector<uint8_t>> appId =
- certParser_.getSwEnforcedBlob(::keymaster::TAG_ATTESTATION_APPLICATION_ID);
- if (appId) {
- EXPECT_EQ(expectedAppId.size(), appId.value().size());
- EXPECT_EQ(0, memcmp(expectedAppId.data(), appId.value().data(), expectedAppId.size()));
- } else {
- // app id not found
- EXPECT_EQ(0, expectedAppId.size());
+ time_t t = timegm(&tm);
+ if (t == -1) {
+ LOG(ERROR) << "Error converting broken-down time to time_t";
+ return false;
}
-
- EXPECT_TRUE(certParser_.getHwEnforcedBool(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
- EXPECT_FALSE(certParser_.getHwEnforcedBool(::keymaster::TAG_INCLUDE_UNIQUE_ID));
-
- // Verify the challenge always matches in size and data of what is passed
- // in.
- vector<uint8_t> attChallenge = certParser_.getAttestationChallenge();
- EXPECT_EQ(expectedChallenge.size(), attChallenge.size());
- EXPECT_EQ(0, memcmp(expectedChallenge.data(), attChallenge.data(), expectedChallenge.size()));
-
- // Ensure the attestation conveys that it's implemented in secure hardware (with carve-out
- // for the reference implementation which cannot be implemented in secure hardware).
- if (hwInfo.credentialStoreName == "Identity Credential Reference Implementation" &&
- hwInfo.credentialStoreAuthorName == "Google") {
- EXPECT_LE(KM_SECURITY_LEVEL_SOFTWARE, certParser_.getKeymasterSecurityLevel());
- EXPECT_LE(KM_SECURITY_LEVEL_SOFTWARE, certParser_.getAttestationSecurityLevel());
-
- } else {
- // Actual devices should use TrustedEnvironment or StrongBox.
- EXPECT_LE(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, certParser_.getKeymasterSecurityLevel());
- EXPECT_LE(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, certParser_.getAttestationSecurityLevel());
- }
+ *outTime = t;
return true;
}
+void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
+ const vector<uint8_t>& expectedChallenge,
+ const vector<uint8_t>& expectedAppId, bool isTestCredential) {
+ ASSERT_GE(credentialKeyCertChain.size(), 2);
+
+ vector<uint8_t> certBytes = credentialKeyCertChain[0].encodedCertificate;
+ const uint8_t* certData = certBytes.data();
+ X509_Ptr cert = X509_Ptr(d2i_X509(nullptr, &certData, certBytes.size()));
+
+ vector<uint8_t> batchCertBytes = credentialKeyCertChain[1].encodedCertificate;
+ const uint8_t* batchCertData = batchCertBytes.data();
+ X509_Ptr batchCert = X509_Ptr(d2i_X509(nullptr, &batchCertData, batchCertBytes.size()));
+
+ // First get some values from the batch certificate which is checked
+ // against the top-level certificate (subject, notAfter)
+ //
+
+ X509_NAME* batchSubject = X509_get_subject_name(batchCert.get());
+ ASSERT_NE(nullptr, batchSubject);
+ time_t batchNotAfter;
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(batchCert.get()), &batchNotAfter));
+
+ // Check all the requirements from IWritableIdentityCredential::getAttestationCertificate()...
+ //
+
+ // - version: INTEGER 2 (means v3 certificate).
+ EXPECT_EQ(2, X509_get_version(cert.get()));
+
+ // - serialNumber: INTEGER 1 (fixed value: same on all certs).
+ EXPECT_EQ(1, ASN1_INTEGER_get(X509_get_serialNumber(cert.get())));
+
+ // - signature: must be set to ECDSA.
+ EXPECT_EQ(NID_ecdsa_with_SHA256, X509_get_signature_nid(cert.get()));
+
+ // - subject: CN shall be set to "Android Identity Credential Key". (fixed value:
+ // same on all certs)
+ X509_NAME* subject = X509_get_subject_name(cert.get());
+ ASSERT_NE(nullptr, subject);
+ EXPECT_EQ("CN=Android Identity Credential Key", x509NameToRfc2253String(subject));
+
+ // - issuer: Same as the subject field of the batch attestation key.
+ X509_NAME* issuer = X509_get_issuer_name(cert.get());
+ ASSERT_NE(nullptr, issuer);
+ EXPECT_EQ(x509NameToRfc2253String(batchSubject), x509NameToRfc2253String(issuer));
+
+ // - validity: Should be from current time and expire at the same time as the
+ // attestation batch certificate used.
+ //
+ // Allow for 10 seconds drift to account for the time drift between Secure HW
+ // and this environment plus the difference between when the certificate was
+ // created and until now
+ //
+ time_t notBefore;
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), ¬Before));
+ uint64_t now = time(nullptr);
+ int64_t diffSecs = now - notBefore;
+ int64_t allowDriftSecs = 10;
+ EXPECT_LE(-allowDriftSecs, diffSecs);
+ EXPECT_GE(allowDriftSecs, diffSecs);
+
+ time_t notAfter;
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), ¬After));
+ EXPECT_EQ(notAfter, batchNotAfter);
+
+ auto [err, attRec] = keymaster::V4_1::parse_attestation_record(certBytes);
+ ASSERT_EQ(keymaster::V4_1::ErrorCode::OK, err);
+
+ // - subjectPublicKeyInfo: must contain attested public key.
+
+ // - The attestationVersion field in the attestation extension must be at least 3.
+ EXPECT_GE(attRec.attestation_version, 3);
+
+ // - The attestationSecurityLevel field must be set to either Software (0),
+ // TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
+ // implemented.
+ EXPECT_GE(attRec.attestation_security_level,
+ keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+ // - The keymasterVersion field in the attestation extension must be set to the.
+ // same value as used for Android Keystore keys.
+ //
+ // Nothing to check here...
+
+ // - The keymasterSecurityLevel field in the attestation extension must be set to
+ // either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
+ // the Trusted Application backing the HAL implementation is implemented.
+ EXPECT_GE(attRec.keymaster_security_level, keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+ // - The attestationChallenge field must be set to the passed-in challenge.
+ EXPECT_EQ(expectedChallenge.size(), attRec.attestation_challenge.size());
+ EXPECT_TRUE(memcmp(expectedChallenge.data(), attRec.attestation_challenge.data(),
+ attRec.attestation_challenge.size()) == 0);
+
+ // - The uniqueId field must be empty.
+ EXPECT_EQ(attRec.unique_id.size(), 0);
+
+ // - The softwareEnforced field in the attestation extension must include
+ // Tag::ATTESTATION_APPLICATION_ID which must be set to the bytes of the passed-in
+ // attestationApplicationId.
+ EXPECT_TRUE(attRec.software_enforced.Contains(keymaster::V4_0::TAG_ATTESTATION_APPLICATION_ID,
+ expectedAppId));
+
+ // - The teeEnforced field in the attestation extension must include
+ //
+ // - Tag::IDENTITY_CREDENTIAL_KEY which indicates that the key is an Identity
+ // Credential key (which can only sign/MAC very specific messages) and not an Android
+ // Keystore key (which can be used to sign/MAC anything). This must not be set
+ // for test credentials.
+ bool hasIcKeyTag =
+ attRec.hardware_enforced.Contains(static_cast<android::hardware::keymaster::V4_0::Tag>(
+ keymaster::V4_1::Tag::IDENTITY_CREDENTIAL_KEY));
+ if (isTestCredential) {
+ EXPECT_FALSE(hasIcKeyTag);
+ } else {
+ EXPECT_TRUE(hasIcKeyTag);
+ }
+
+ // - Tag::PURPOSE must be set to SIGN
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_PURPOSE,
+ keymaster::V4_0::KeyPurpose::SIGN));
+
+ // - Tag::KEY_SIZE must be set to the appropriate key size, in bits (e.g. 256)
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_KEY_SIZE, 256));
+
+ // - Tag::ALGORITHM must be set to EC
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_ALGORITHM,
+ keymaster::V4_0::Algorithm::EC));
+
+ // - Tag::NO_AUTH_REQUIRED must be set
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_NO_AUTH_REQUIRED));
+
+ // - Tag::DIGEST must be include SHA_2_256
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_DIGEST,
+ keymaster::V4_0::Digest::SHA_2_256));
+
+ // - Tag::EC_CURVE must be set to P_256
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_EC_CURVE,
+ keymaster::V4_0::EcCurve::P_256));
+
+ // - Tag::ROOT_OF_TRUST must be set
+ //
+ EXPECT_GE(attRec.root_of_trust.security_level,
+ keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+ // - Tag::OS_VERSION and Tag::OS_PATCHLEVEL must be set
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_VERSION));
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_PATCHLEVEL));
+
+ // TODO: we could retrieve osVersion and osPatchLevel from Android itself and compare it
+ // with what was reported in the certificate.
+}
+
+void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain) {
+ const uint8_t* data = authKeyCertChain.data();
+ auto cert = X509_Ptr(d2i_X509(nullptr, &data, authKeyCertChain.size()));
+
+ // - version: INTEGER 2 (means v3 certificate).
+ EXPECT_EQ(X509_get_version(cert.get()), 2);
+
+ // - serialNumber: INTEGER 1 (fixed value: same on all certs).
+ EXPECT_EQ(ASN1_INTEGER_get(X509_get_serialNumber(cert.get())), 1);
+
+ // - signature: must be set to ECDSA.
+ EXPECT_EQ(X509_get_signature_nid(cert.get()), NID_ecdsa_with_SHA256);
+
+ // - subject: CN shall be set to "Android Identity Credential Authentication Key". (fixed
+ // value: same on all certs)
+ X509_NAME* subject = X509_get_subject_name(cert.get());
+ ASSERT_NE(subject, nullptr);
+ EXPECT_EQ(x509NameToRfc2253String(subject),
+ "CN=Android Identity Credential Authentication Key");
+
+ // - issuer: CN shall be set to "Android Identity Credential Key". (fixed value:
+ // same on all certs)
+ X509_NAME* issuer = X509_get_issuer_name(cert.get());
+ ASSERT_NE(issuer, nullptr);
+ EXPECT_EQ(x509NameToRfc2253String(issuer), "CN=Android Identity Credential Key");
+
+ // - subjectPublicKeyInfo: must contain attested public key.
+
+ // - validity: should be from current time and one year in the future (365 days).
+ time_t notBefore, notAfter;
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), ¬After));
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), ¬Before));
+
+ // Allow for 10 seconds drift to account for the time drift between Secure HW
+ // and this environment plus the difference between when the certificate was
+ // created and until now
+ //
+ uint64_t now = time(nullptr);
+ int64_t diffSecs = now - notBefore;
+ int64_t allowDriftSecs = 10;
+ EXPECT_LE(-allowDriftSecs, diffSecs);
+ EXPECT_GE(allowDriftSecs, diffSecs);
+ constexpr uint64_t kSecsInOneYear = 365 * 24 * 60 * 60;
+ EXPECT_EQ(notBefore + kSecsInOneYear, notAfter);
+}
+
vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries) {
vector<RequestNamespace> ret;
RequestNamespace curNs;
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/VtsIdentityTestUtils.h
index 673b736..85c24f8 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.h
+++ b/identity/aidl/vts/VtsIdentityTestUtils.h
@@ -34,8 +34,8 @@
struct AttestationData {
AttestationData(sp<IWritableIdentityCredential>& writableCredential, string challenge,
- vector<uint8_t> applicationId)
- : attestationApplicationId(applicationId) {
+ vector<uint8_t> attestationAppId)
+ : attestationApplicationId(attestationAppId) {
// ASSERT_NE(writableCredential, nullptr);
if (!challenge.empty()) {
@@ -94,7 +94,7 @@
};
bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
- sp<IIdentityCredentialStore>& credentialStore);
+ sp<IIdentityCredentialStore>& credentialStore, bool testCredential);
optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal);
@@ -111,13 +111,17 @@
void setImageData(vector<uint8_t>& image);
-bool validateAttestationCertificate(const vector<Certificate>& inputCertificates,
+void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
const vector<uint8_t>& expectedChallenge,
- const vector<uint8_t>& expectedAppId,
- const HardwareInformation& hwInfo);
+ const vector<uint8_t>& expectedAppId, bool isTestCredential);
vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries);
+// Verifies that the X.509 certificate for a just created authentication key
+// is valid.
+//
+void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain);
+
} // namespace android::hardware::identity::test_utils
#endif // VTS_IDENTITY_TEST_UTILS_H
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index f7ec7c5..3aa5bb6 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -35,6 +35,9 @@
using ::std::vector;
using ::std::pair;
+// The semantic tag for a bstr which includes Encoded CBOR (RFC 7049, section 2.4)
+const int kSemanticTagEncodedCbor = 24;
+
// ---------------------------------------------------------------------------
// Miscellaneous utilities.
// ---------------------------------------------------------------------------
@@ -108,45 +111,47 @@
// ---------------------------------------------------------------------------
// EC crypto functionality / abstraction (only supports P-256).
// ---------------------------------------------------------------------------
+
// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
-// PKCS#8 encoded key-pair. Also generates an attestation
-// certificate using the |challenge| and |applicationId|, and returns the generated
-// certificate in X.509 certificate chain format.
+// DER encoded private key. Also generates an attestation using the |challenge|
+// and |applicationId|, and returns the generated certificate chain.
//
-// The attestation time fields used will be the current time, and expires in one year.
+// The notBeffore field will be the current time and the notAfter will be the same
+// same time as the batch certificate.
//
// The first parameter of the return value is the keyPair generated, second return in
// the pair is the attestation certificate generated.
-optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
- const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId);
-
-// Like createEcKeyPairAndAttestation() but allows you to choose the public key.
//
+optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
+ const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
+ bool isTestCredential);
+
+// (TODO: remove when no longer used by 3rd party.)
optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge,
const vector<uint8_t>& applicationId);
// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
-// PKCS#8 encoded key-pair.
+// private key in DER format (as specified in RFC 5915).
//
optional<vector<uint8_t>> createEcKeyPair();
-// For an EC key |keyPair| encoded in PKCS#8 format, extracts the public key in
+// For an EC key |keyPair| encoded in DER format, extracts the public key in
// uncompressed point form.
//
optional<vector<uint8_t>> ecKeyPairGetPublicKey(const vector<uint8_t>& keyPair);
-// For an EC key |keyPair| encoded in PKCS#8 format, extracts the private key as
+// For an EC key |keyPair| encoded in DER format, extracts the private key as
// an EC uncompressed key.
//
optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair);
-// Creates a PKCS#8 encoded key-pair from a private key (which must be uncompressed,
-// e.g. 32 bytes). The public key is derived from the given private key..
+// Creates a DER encoded representation from a private key (which must be uncompressed,
+// e.g. 32 bytes).
//
optional<vector<uint8_t>> ecPrivateKeyToKeyPair(const vector<uint8_t>& privateKey);
-// For an EC key |keyPair| encoded in PKCS#8 format, creates a PKCS#12 structure
+// For an EC key |keyPair| encoded in DER format, creates a PKCS#12 structure
// with the key-pair (not using a password to encrypt the data). The public key
// in the created structure is included as a certificate, using the given fields
// |serialDecimal|, |issuer|, |subject|, |validityNotBefore|, and
@@ -209,6 +214,13 @@
//
optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate);
+// Extracts notBefore and notAfter from the top-most certificate in |certificateChain
+// (which should be a concatenated chain of DER-encoded X.509 certificates).
+//
+// Returns notBefore and notAfter in that order.
+//
+optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x509Certificate);
+
// Generates a X.509 certificate for |publicKey| (which must be in the format
// returned by ecKeyPairGetPublicKey()).
//
@@ -351,6 +363,15 @@
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
+optional<vector<uint8_t>> calcMac(const vector<uint8_t>& sessionTranscriptEncoded,
+ const string& docType,
+ const vector<uint8_t>& deviceNameSpacesEncoded,
+ const vector<uint8_t>& eMacKey);
+
+optional<vector<uint8_t>> calcEMacKey(const vector<uint8_t>& privateKey,
+ const vector<uint8_t>& publicKey,
+ const vector<uint8_t>& sessionTranscriptBytes);
+
// Returns the testing AES-128 key where all bits are set to 0.
const vector<uint8_t>& getTestHardwareBoundKey();
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 747f182..57cdc98 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -44,6 +44,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <charconv>
#include <cppbor.h>
#include <cppbor_parse.h>
@@ -54,6 +55,7 @@
#include <keymaster/contexts/soft_attestation_cert.h>
#include <keymaster/keymaster_tags.h>
#include <keymaster/km_openssl/attestation_utils.h>
+#include <keymaster/km_openssl/certificate_utils.h>
namespace android {
namespace hardware {
@@ -870,16 +872,109 @@
return hmac;
}
+int parseDigits(const char** s, int numDigits) {
+ int result;
+ auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
+ if (ec != std::errc()) {
+ LOG(ERROR) << "Error parsing " << numDigits << " digits "
+ << " from " << s;
+ return 0;
+ }
+ *s += numDigits;
+ return result;
+}
+
+bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
+ struct tm tm;
+
+ memset(&tm, '\0', sizeof(tm));
+ const char* timeStr = (const char*)asn1Time->data;
+ const char* s = timeStr;
+ if (asn1Time->type == V_ASN1_UTCTIME) {
+ tm.tm_year = parseDigits(&s, 2);
+ if (tm.tm_year < 70) {
+ tm.tm_year += 100;
+ }
+ } else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
+ tm.tm_year = parseDigits(&s, 4) - 1900;
+ tm.tm_year -= 1900;
+ } else {
+ LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
+ return false;
+ }
+ tm.tm_mon = parseDigits(&s, 2) - 1;
+ tm.tm_mday = parseDigits(&s, 2);
+ tm.tm_hour = parseDigits(&s, 2);
+ tm.tm_min = parseDigits(&s, 2);
+ tm.tm_sec = parseDigits(&s, 2);
+ // This may need to be updated if someone create certificates using +/- instead of Z.
+ //
+ if (*s != 'Z') {
+ LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
+ return false;
+ }
+
+ time_t t = timegm(&tm);
+ if (t == -1) {
+ LOG(ERROR) << "Error converting broken-down time to time_t";
+ return false;
+ }
+ *outTime = t;
+ return true;
+}
+
// Generates the attestation certificate with the parameters passed in. Note
// that the passed in |activeTimeMilliSeconds| |expireTimeMilliSeconds| are in
// milli seconds since epoch. We are setting them to milliseconds due to
// requirement in AuthorizationSet KM_DATE fields. The certificate created is
// actually in seconds.
-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) {
+//
+// If 0 is passed for expiration time, the expiration time from batch
+// certificate will be used.
+//
+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";
+ return {};
+ }
+ if (expireTimeMilliSeconds == 0) {
+ 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]);
+ const uint8_t* bcData = bcBlob->data;
+ auto bc = X509_Ptr(d2i_X509(nullptr, &bcData, bcBlob->data_length));
+ time_t bcNotAfter;
+ if (!parseAsn1Time(X509_get0_notAfter(bc.get()), &bcNotAfter)) {
+ LOG(ERROR) << "Error getting notAfter from batch certificate";
+ return {};
+ }
+ expireTimeMilliSeconds = bcNotAfter * 1000;
+ }
+ const keymaster_key_blob_t* attestation_signing_key =
+ ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
+ if (attestation_signing_key == nullptr) {
+ LOG(ERROR) << "Error getting attestation key";
+ return {};
+ }
+
+ ::keymaster::X509_NAME_Ptr subjectName;
+ if (KM_ERROR_OK !=
+ ::keymaster::make_name_from_str("Android Identity Credential Key", &subjectName)) {
+ LOG(ERROR) << "Cannot create attestation subject";
+ return {};
+ }
+
+ vector<uint8_t> subject(i2d_X509_NAME(subjectName.get(), NULL));
+ unsigned char* subjectPtr = subject.data();
+
+ i2d_X509_NAME(subjectName.get(), &subjectPtr);
+
::keymaster::AuthorizationSet auth_set(
::keymaster::AuthorizationSetBuilder()
.Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(),
@@ -894,6 +989,8 @@
// includes app id.
.Authorization(::keymaster::TAG_ATTESTATION_APPLICATION_ID,
applicationId.data(), applicationId.size())
+ .Authorization(::keymaster::TAG_CERTIFICATE_SUBJECT, subject.data(),
+ subject.size())
.Authorization(::keymaster::TAG_USAGE_EXPIRE_DATETIME, expireTimeMilliSeconds));
// Unique id and device id is not applicable for identity credential attestation,
@@ -901,7 +998,7 @@
::keymaster::AuthorizationSet swEnforced(::keymaster::AuthorizationSetBuilder().Authorization(
::keymaster::TAG_CREATION_DATETIME, activeTimeMilliSeconds));
- ::keymaster::AuthorizationSet hwEnforced(
+ ::keymaster::AuthorizationSetBuilder hwEnforcedBuilder =
::keymaster::AuthorizationSetBuilder()
.Authorization(::keymaster::TAG_PURPOSE, KM_PURPOSE_SIGN)
.Authorization(::keymaster::TAG_KEY_SIZE, 256)
@@ -909,33 +1006,27 @@
.Authorization(::keymaster::TAG_NO_AUTH_REQUIRED)
.Authorization(::keymaster::TAG_DIGEST, KM_DIGEST_SHA_2_256)
.Authorization(::keymaster::TAG_EC_CURVE, KM_EC_CURVE_P_256)
- .Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
+ .Authorization(::keymaster::TAG_OS_VERSION, 42)
+ .Authorization(::keymaster::TAG_OS_PATCHLEVEL, 43);
- const keymaster_cert_chain_t* attestation_chain =
- ::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
-
- if (attestation_chain == nullptr) {
- LOG(ERROR) << "Error getting attestation chain";
- return {};
+ // Only include TAG_IDENTITY_CREDENTIAL_KEY if it's not a test credential
+ if (!isTestCredential) {
+ hwEnforcedBuilder.Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY);
}
-
- const keymaster_key_blob_t* attestation_signing_key =
- ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
- if (attestation_signing_key == nullptr) {
- LOG(ERROR) << "Error getting attestation key";
- return {};
- }
+ ::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
keymaster_error_t error;
::keymaster::CertChainPtr cert_chain_out;
- ::keymaster::PureSoftKeymasterContext context;
- // set identity version to 10 per hal requirements specified in IWriteableCredential.hal
- // For now, the identity version in the attestation is set in the keymaster
- // version field in the portable keymaster lib, which is a bit misleading.
- uint identity_version = 10;
+ // 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(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
+
error = generate_attestation_from_EVP(key, swEnforced, hwEnforced, auth_set, context,
- identity_version, *attestation_chain,
+ ::keymaster::kCurrentKeymasterVersion, *attestation_chain,
*attestation_signing_key, &cert_chain_out);
if (KM_ERROR_OK != error || !cert_chain_out) {
@@ -957,7 +1048,8 @@
}
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
- const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
+ const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
+ bool isTestCredential) {
auto ec_key = ::keymaster::EC_KEY_Ptr(EC_KEY_new());
auto pkey = ::keymaster::EVP_PKEY_Ptr(EVP_PKEY_new());
auto group = ::keymaster::EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
@@ -978,12 +1070,11 @@
return {};
}
- uint64_t now = time(nullptr);
- uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
- uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
+ uint64_t nowMs = time(nullptr) * 1000;
+ uint64_t expireTimeMs = 0; // Set to same as batch certificate
- optional<vector<vector<uint8_t>>> attestationCert =
- createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
+ optional<vector<vector<uint8_t>>> attestationCert = createAttestation(
+ pkey.get(), applicationId, challenge, nowMs, expireTimeMs, isTestCredential);
if (!attestationCert) {
LOG(ERROR) << "Error create attestation from key and challenge";
return {};
@@ -1031,14 +1122,12 @@
return {};
}
- uint64_t now = (std::chrono::duration_cast<std::chrono::nanoseconds>(
- std::chrono::system_clock::now().time_since_epoch()).
- count()/ 1000000000);
- uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
- uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
+ uint64_t nowMs = time(nullptr) * 1000;
+ uint64_t expireTimeMs = 0; // Set to same as batch certificate
optional<vector<vector<uint8_t>>> attestationCert =
- createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
+ createAttestation(pkey.get(), applicationId, challenge, nowMs, expireTimeMs,
+ false /* isTestCredential */);
if (!attestationCert) {
LOG(ERROR) << "Error create attestation from key and challenge";
return {};
@@ -1646,6 +1735,32 @@
return std::make_pair(tbsCertificateOffset, tbsCertificateSize);
}
+optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x509Certificate) {
+ vector<X509_Ptr> certs;
+ if (!parseX509Certificates(x509Certificate, certs)) {
+ LOG(ERROR) << "Error parsing certificates";
+ return {};
+ }
+ if (certs.size() < 1) {
+ LOG(ERROR) << "No certificates in chain";
+ return {};
+ }
+
+ time_t notBefore;
+ time_t notAfter;
+ if (!parseAsn1Time(X509_get0_notBefore(certs[0].get()), ¬Before)) {
+ LOG(ERROR) << "Error parsing notBefore";
+ return {};
+ }
+
+ if (!parseAsn1Time(X509_get0_notAfter(certs[0].get()), ¬After)) {
+ LOG(ERROR) << "Error parsing notAfter";
+ return {};
+ }
+
+ return std::make_pair(notBefore, notAfter);
+}
+
optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate) {
vector<X509_Ptr> certs;
if (!parseX509Certificates(x509Certificate, certs)) {
@@ -2218,6 +2333,49 @@
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
+optional<vector<uint8_t>> calcEMacKey(const vector<uint8_t>& privateKey,
+ const vector<uint8_t>& publicKey,
+ const vector<uint8_t>& sessionTranscriptBytes) {
+ optional<vector<uint8_t>> sharedSecret = support::ecdh(publicKey, privateKey);
+ if (!sharedSecret) {
+ LOG(ERROR) << "Error performing ECDH";
+ return {};
+ }
+ vector<uint8_t> salt = support::sha256(sessionTranscriptBytes);
+ vector<uint8_t> info = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+ optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+ if (!derivedKey) {
+ LOG(ERROR) << "Error performing HKDF";
+ return {};
+ }
+ return derivedKey.value();
+}
+
+optional<vector<uint8_t>> calcMac(const vector<uint8_t>& sessionTranscriptEncoded,
+ const string& docType,
+ const vector<uint8_t>& deviceNameSpacesEncoded,
+ const vector<uint8_t>& eMacKey) {
+ auto [sessionTranscriptItem, _, errMsg] = cppbor::parse(sessionTranscriptEncoded);
+ if (sessionTranscriptItem == nullptr) {
+ LOG(ERROR) << "Error parsing sessionTranscriptEncoded: " << errMsg;
+ return {};
+ }
+ // The data that is MACed is ["DeviceAuthentication", sessionTranscript, docType,
+ // deviceNameSpacesBytes] so build up that structure
+ cppbor::Array deviceAuthentication =
+ cppbor::Array()
+ .add("DeviceAuthentication")
+ .add(std::move(sessionTranscriptItem))
+ .add(docType)
+ .add(cppbor::Semantic(kSemanticTagEncodedCbor, deviceNameSpacesEncoded));
+ vector<uint8_t> deviceAuthenticationBytes =
+ cppbor::Semantic(kSemanticTagEncodedCbor, deviceAuthentication.encode()).encode();
+ optional<vector<uint8_t>> calculatedMac =
+ support::coseMac0(eMacKey, {}, // payload
+ deviceAuthenticationBytes); // detached content
+ return calculatedMac;
+}
+
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) {
vector<vector<uint8_t>> ret;
diff --git a/identity/support/tests/IdentityCredentialSupportTest.cpp b/identity/support/tests/IdentityCredentialSupportTest.cpp
index c356549..266f263 100644
--- a/identity/support/tests/IdentityCredentialSupportTest.cpp
+++ b/identity/support/tests/IdentityCredentialSupportTest.cpp
@@ -436,6 +436,300 @@
support::cborPrettyPrint(mac.value()));
}
+// Generates a private key in DER format for a small value of 'd'.
+//
+// Used for test vectors.
+//
+vector<uint8_t> p256PrivateKeyFromD(uint8_t d) {
+ vector<uint8_t> privateUncompressed;
+ privateUncompressed.resize(32);
+ privateUncompressed[31] = d;
+ optional<vector<uint8_t>> privateKey = support::ecPrivateKeyToKeyPair(privateUncompressed);
+ return privateKey.value();
+}
+
+std::pair<vector<uint8_t>, vector<uint8_t>> p256PrivateKeyGetXandY(
+ const vector<uint8_t> privateKey) {
+ optional<vector<uint8_t>> publicUncompressed = support::ecKeyPairGetPublicKey(privateKey);
+ vector<uint8_t> x = vector<uint8_t>(publicUncompressed.value().begin() + 1,
+ publicUncompressed.value().begin() + 33);
+ vector<uint8_t> y = vector<uint8_t>(publicUncompressed.value().begin() + 33,
+ publicUncompressed.value().begin() + 65);
+ return std::make_pair(x, y);
+}
+
+const cppbor::Item* findValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ // TODO: Need cast until libcppbor's Map::get() is marked as const
+ auto [item, found] = ((cppbor::Map*)map)->get(keyValue);
+ if (!found) {
+ return nullptr;
+ }
+ return item.get();
+}
+
+const cppbor::Array* findArrayValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ const cppbor::Item* item = findValueForTstr(map, keyValue);
+ if (item == nullptr) {
+ return nullptr;
+ }
+ return item->asArray();
+}
+
+const cppbor::Map* findMapValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ const cppbor::Item* item = findValueForTstr(map, keyValue);
+ if (item == nullptr) {
+ return nullptr;
+ }
+ return item->asMap();
+}
+
+const cppbor::Semantic* findSemanticValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ const cppbor::Item* item = findValueForTstr(map, keyValue);
+ if (item == nullptr) {
+ return nullptr;
+ }
+ return item->asSemantic();
+}
+
+const std::string findStringValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ const cppbor::Item* item = findValueForTstr(map, keyValue);
+ if (item == nullptr) {
+ return nullptr;
+ }
+ const cppbor::Tstr* tstr = item->asTstr();
+ if (tstr == nullptr) {
+ return "";
+ }
+ return tstr->value();
+}
+
+TEST(IdentityCredentialSupport, testVectors_18013_5) {
+ // This is a test against known vectors for ISO 18013-5.
+ //
+ // The objective of this test is to verify that support::calcEMacKey() and
+ // support::calcMac() agree with the given test vectors.
+ //
+
+ // We're given static device key:
+ //
+ // x: 28412803729898893058558238221310261427084375743576167377786533380249859400145
+ // y: 65403602826180996396520286939226973026599920614829401631985882360676038096704
+ // d: 11
+ //
+ vector<uint8_t> deviceKey = p256PrivateKeyFromD(11);
+ auto [deviceKeyX, deviceKeyY] = p256PrivateKeyGetXandY(deviceKey);
+ EXPECT_EQ(support::encodeHex(deviceKeyX),
+ "3ed113b7883b4c590638379db0c21cda16742ed0255048bf433391d374bc21d1");
+ EXPECT_EQ(support::encodeHex(deviceKeyY),
+ "9099209accc4c8a224c843afa4f4c68a090d04da5e9889dae2f8eefce82a3740");
+
+ // We're given Ephemeral reader key:
+ //
+ // x: 59535862115950685744176693329402396749019581632805653266809849538337418304154
+ // y: 53776829996815113213100700404832701936765102413212294632483274374518863708344
+ // d: 20
+ //
+ vector<uint8_t> ephemeralReaderKey = p256PrivateKeyFromD(20);
+ auto [ephemeralReaderKeyX, ephemeralReaderKeyY] = p256PrivateKeyGetXandY(ephemeralReaderKey);
+ EXPECT_EQ(support::encodeHex(ephemeralReaderKeyX),
+ "83a01a9378395bab9bcd6a0ad03cc56d56e6b19250465a94a234dc4c6b28da9a");
+ EXPECT_EQ(support::encodeHex(ephemeralReaderKeyY),
+ "76e49b6de2f73234ae6a5eb9d612b75c9f2202bb6923f54ff8240aaa86f640b8");
+ vector<uint8_t> ephemeralReaderKeyPublic =
+ support::ecKeyPairGetPublicKey(ephemeralReaderKey).value();
+
+ // We're given SessionEstablishment.
+ //
+ // SessionEstablishment = {
+ // "eReaderKey" : EReaderKeyBytes,
+ // "data" : bstr ; Encrypted mdoc request
+ // }
+ //
+ // Fish out EReaderKey from this.
+ //
+ // Note that the test vector below is incorrect insofar that it uses
+ // "eReaderKeyBytes" instead of just "eReaderKey". This will be corrected in
+ // the future.
+ //
+ optional<vector<uint8_t>> sessionEstablishmentEncoded = support::decodeHex(
+ "a26f655265616465724b65794279746573d818584ba40102200121582083a01a9378395bab9bcd6a0ad03c"
+ "c56d56e6b19250465a94a234dc4c6b28da9a22582076e49b6de2f73234ae6a5eb9d612b75c9f2202bb6923"
+ "f54ff8240aaa86f640b864646174615902d945b31040c57491acb6d46a71f6c1f67a0b837df1bda9089fd0"
+ "3d0b1fdac3eeb2874a4ef6f90c97d03397186ba00a91102faae7e992e15f761d5662c3c37e3c6c2cfd2ebc"
+ "0bf59dbb8795e377bd7dd353230a41ba2d82294b45871a39b42ca531f26b52f46e356fbaf5075c8fd5b8b0"
+ "8a0df4a1d2e1bdd2e5d69169c1efbb51e393e608d833d325bebfbccb2e15ec08f94b264582fa7b93f7cebc"
+ "aa69f4f0cac2744d4fe35b04df26b2ae69273eed33024949080c1c95a6ef046beede959e9494297dd770af"
+ "4ac6fdd56783aa012555c213dc05cf0f41d1c95119720fcfe1621027f80e2ddd56ea3c1fc596f7b2579333"
+ "5a887ec788092b4a69d23b6219e27d0249b50b3fdcb95b5227007689362e0416b3bae3dae7cb56b4394666"
+ "4e3a3f60dce8d0b678fcd754bebf87bd2b0278dd782d952488a46f2874e34c2dd97bb74084a62b850e9719"
+ "252cd1dca7dbf1858193f6cf093cb3735312bbe1138cf29d8f350e285923f8ef07065299926720b42264e8"
+ "fd5d4b133e72f47c4e999ea689c353f8b41e50a59838e1a0d09eca4a557f77a9c389a0591ad1639119ce86"
+ "edc3320130480ee5101effae6066e8c85aac9ead2ae83e49c1e508aab02f753decbb522ea2200d62fd5d26"
+ "094bd35100bffaa1cdc6af9f7e9cfe7b63da6b5671cd5ac2cf5da450c72addc64cde441f3b7f7fdaf930ad"
+ "1e13388e8a7308d8ca4607e59e082db431a232e7e12cb692baeb4b2127e110ff24cea322ffdbc2e4d9c4c6"
+ "bed27753137d07897c8613627a799a560cf1a2d1edb3de029442862940a5ed7785eea8b6ace93aa6af0792"
+ "fd82877f62d07b757d0179ecbb7347004ecc9c0690d41f75f188cb17ffd2cec2ad8c9675466bb33b737a2a"
+ "e7592b2dcb8132aced2e572266f3f5413a5f9d6d4339a1e4662622af2e7e157a4ea3bfd5c4247e2ec91d8c"
+ "5c3c17427d5edfae673d0e0f782a8d40fa805fd8bc82ae3cb21a65cdad863e02309f6b01d1753fa884b778"
+ "f6e019a2004d8964deeb11f1fd478fcb");
+ ASSERT_TRUE(sessionEstablishmentEncoded);
+ auto [sessionEstablishmentItem, _se, _se2] = cppbor::parse(sessionEstablishmentEncoded.value());
+ const cppbor::Map* sessionEstablishment = sessionEstablishmentItem->asMap();
+ ASSERT_NE(sessionEstablishment, nullptr);
+ const cppbor::Semantic* eReaderKeyBytes =
+ findSemanticValueForTstr(sessionEstablishment, "eReaderKeyBytes");
+ ASSERT_NE(eReaderKeyBytes, nullptr);
+ ASSERT_EQ(eReaderKeyBytes->value(), 24);
+ const cppbor::Bstr* eReaderKeyBstr = eReaderKeyBytes->child()->asBstr();
+ ASSERT_NE(eReaderKeyBstr, nullptr);
+ vector<uint8_t> eReaderKeyEncoded = eReaderKeyBstr->value();
+ // TODO: verify this agrees with ephemeralReaderKeyX and ephemeralReaderKeyY
+
+ // We're given DeviceEngagement.
+ //
+ vector<uint8_t> deviceEngagementEncoded =
+ support::decodeHex(
+ "a20063312e30018201d818584ba401022001215820cef66d6b2a3a993e591214d1ea223fb545ca"
+ "6c471c48306e4c36069404c5723f225820878662a229aaae906e123cdd9d3b4c10590ded29fe75"
+ "1eeeca34bbaa44af0773")
+ .value();
+
+ // Now calculate SessionTranscriptBytes. It is defined as
+ //
+ // SessionTranscript = [
+ // DeviceEngagementBytes,
+ // EReaderKeyBytes,
+ // Handover
+ // ]
+ //
+ // SessionTranscriptBytes = #6.24(bstr .cbor SessionTranscript)
+ //
+ cppbor::Array sessionTranscript;
+ sessionTranscript.add(cppbor::Semantic(24, deviceEngagementEncoded));
+ sessionTranscript.add(cppbor::Semantic(24, eReaderKeyEncoded));
+ sessionTranscript.add(cppbor::Null());
+ vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
+ vector<uint8_t> sessionTranscriptBytes =
+ cppbor::Semantic(24, sessionTranscriptEncoded).encode();
+
+ // The expected EMacKey is 4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2
+ //
+ // Verify that support::calcEMacKey() gets the same result.
+ //
+ optional<vector<uint8_t>> eMacKey =
+ support::calcEMacKey(support::ecKeyPairGetPrivateKey(deviceKey).value(), // private key
+ ephemeralReaderKeyPublic, // public key
+ sessionTranscriptBytes); // sessionTranscriptBytes
+ ASSERT_TRUE(eMacKey);
+ ASSERT_EQ(support::encodeHex(eMacKey.value()),
+ "4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2");
+
+ // Also do it the other way around
+ //
+ optional<vector<uint8_t>> eMacKey2 = support::calcEMacKey(
+ support::ecKeyPairGetPrivateKey(ephemeralReaderKey).value(), // private key
+ support::ecKeyPairGetPublicKey(deviceKey).value(), // public key
+ sessionTranscriptBytes); // sessionTranscriptBytes
+ ASSERT_TRUE(eMacKey2);
+ ASSERT_EQ(support::encodeHex(eMacKey2.value()),
+ "4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2");
+
+ // We're given DeviceResponse
+ //
+ vector<uint8_t> deviceResponseEncoded =
+ support::decodeHex(
+ "a36776657273696f6e63312e3069646f63756d656e747381a367646f6354797065756f72672e69"
+ "736f2e31383031332e352e312e6d444c6c6973737565725369676e6564a26a6e616d6553706163"
+ "6573a2716f72672e69736f2e31383031332e352e3181d8185863a4686469676573744944016672"
+ "616e646f6d58208798645b20ea200e19ffabac92624bee6aec63aceedecfb1b80077d22bfc20e9"
+ "71656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456"
+ "616c756563446f656b636f6d2e6578616d706c6581d8185864a468646967657374494401667261"
+ "6e646f6d5820218ecf13521b53f4b96abaebe56417afec0e4c91fc8fb26086cd1e5cdc1a94ff71"
+ "656c656d656e744964656e7469666965726f616e6f746865725f656c656d656e746c656c656d65"
+ "6e7456616c75650a6a697373756572417574688443a10126a118215901d2308201ce30820174a0"
+ "0302010202141f7d44f4f107c5ee3f566049cf5d72de294b0d23300a06082a8648ce3d04030230"
+ "233114301206035504030c0b75746f7069612069616361310b3009060355040613025553301e17"
+ "0d3230313030313030303030305a170d3231313030313030303030305a30213112301006035504"
+ "030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106"
+ "082a8648ce3d03010703420004301d9e502dc7e05da85da026a7ae9aa0fac9db7d52a95b3e3e3f"
+ "9aa0a1b45b8b6551b6f6b3061223e0d23c026b017d72298d9ae46887ca61d58db6aea17ee267a3"
+ "8187308184301e0603551d120417301581136578616d706c65406578616d706c652e636f6d301c"
+ "0603551d1f041530133011a00fa00d820b6578616d706c652e636f6d301d0603551d0e04160414"
+ "7bef4db59a1ffb07592bfc57f4743b8a73aea792300e0603551d0f0101ff040403020780301506"
+ "03551d250101ff040b3009060728818c5d050102300a06082a8648ce3d04030203480030450220"
+ "21d52fb1fbda80e5bfda1e8dfb1bc7bf0acb7261d5c9ff54425af76eb21571c602210082bf301f"
+ "89e0a2cb9ca9c9050352de80b47956764f7a3e07bf6a8cd87528a3b55901d2d8185901cda66776"
+ "657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c75"
+ "6544696765737473a2716f72672e69736f2e31383031332e352e31a20058203b22af1126771f02"
+ "f0ea0d546d4ee3c5b51637381154f5211b79daf5f9facaa8015820f2cba0ce3cde5df901a3da75"
+ "13a4d7f7225fdfe5a306544529bf3dbcce655ca06b636f6d2e6578616d706c65a200582072636d"
+ "ddc282424a63499f4b3927aaa3b74da7b9c0134178bf735e949e4a761e01582006322d3cbe6603"
+ "876bdacc5b6679b51b0fc53d029c244fd5ea719d9028459c916d6465766963654b6579496e666f"
+ "a1696465766963654b6579a4010220012158203ed113b7883b4c590638379db0c21cda16742ed0"
+ "255048bf433391d374bc21d12258209099209accc4c8a224c843afa4f4c68a090d04da5e9889da"
+ "e2f8eefce82a374067646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c"
+ "76616c6964697479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a"
+ "30325a6976616c696446726f6dc074323032302d31302d30315431333a33303a30325a6a76616c"
+ "6964556e74696cc074323032312d31302d30315431333a33303a30325a5840273ec1b59817d571"
+ "b5a2c5c0ab0ea213d42acb18547fd7097afcc888a22ecbb863c6461ce0e240880895b4aaa84308"
+ "784571c7be7aa3a2e7e3a2ea1a145ed1966c6465766963655369676e6564a26a6e616d65537061"
+ "636573d81841a06a64657669636541757468a1696465766963654d61638443a10105a0f6582009"
+ "da7c964ac004ec36ec64edd0c1abf50c03433c215c3ddb144768abcdf20a60667374617475730"
+ "0")
+ .value();
+ auto [deviceResponseItem, _, _2] = cppbor::parse(deviceResponseEncoded);
+ const cppbor::Map* deviceResponse = deviceResponseItem->asMap();
+ ASSERT_NE(deviceResponse, nullptr);
+ const cppbor::Array* documents = findArrayValueForTstr(deviceResponse, "documents");
+ ASSERT_NE(documents, nullptr);
+ ASSERT_EQ(documents->size(), 1);
+ const cppbor::Map* document = ((*documents)[0])->asMap();
+ ASSERT_NE(document, nullptr);
+
+ // Get docType
+ string docType = findStringValueForTstr(document, "docType");
+ ASSERT_EQ(docType, "org.iso.18013.5.1.mDL");
+
+ // Drill down...
+ const cppbor::Map* deviceSigned = findMapValueForTstr(document, "deviceSigned");
+ ASSERT_NE(deviceSigned, nullptr);
+
+ // Dig out the encoded form of DeviceNameSpaces
+ //
+ const cppbor::Semantic* deviceNameSpacesBytes =
+ findSemanticValueForTstr(deviceSigned, "nameSpaces");
+ ASSERT_NE(deviceNameSpacesBytes, nullptr);
+ ASSERT_EQ(deviceNameSpacesBytes->value(), 24);
+ const cppbor::Bstr* deviceNameSpacesBstr = deviceNameSpacesBytes->child()->asBstr();
+ ASSERT_NE(deviceNameSpacesBstr, nullptr);
+ vector<uint8_t> deviceNameSpacesEncoded = deviceNameSpacesBstr->value();
+
+ // (For this version of 18013-5, DeviceNameSpaces is always supposed to be empty, check that.)
+ EXPECT_EQ(deviceNameSpacesEncoded, cppbor::Map().encode());
+
+ const cppbor::Map* deviceAuth = findMapValueForTstr(deviceSigned, "deviceAuth");
+ ASSERT_NE(deviceAuth, nullptr);
+ // deviceMac is is the COSE_Mac0.. dig out the encoded form to check that
+ // support::calcMac() gives exactly the same bytes.
+ //
+ const cppbor::Array* deviceMac = findArrayValueForTstr(deviceAuth, "deviceMac");
+ ASSERT_NE(deviceMac, nullptr);
+ vector<uint8_t> deviceMacEncoded = deviceMac->encode();
+
+ // Now we calculate what it should be..
+ optional<vector<uint8_t>> calculatedMac =
+ support::calcMac(sessionTranscriptEncoded, // SessionTranscript
+ docType, // DocType
+ deviceNameSpacesEncoded, // DeviceNamespaces
+ eMacKey.value()); // EMacKey
+ ASSERT_TRUE(calculatedMac);
+
+ // ... and hopefully it's the same!
+ ASSERT_EQ(calculatedMac.value().size(), deviceMacEncoded.size());
+ EXPECT_TRUE(memcmp(calculatedMac.value().data(), deviceMacEncoded.data(),
+ deviceMacEncoded.size()) == 0);
+}
+
} // namespace identity
} // namespace hardware
} // namespace android
diff --git a/keymaster/3.0/IKeymasterDevice.hal b/keymaster/3.0/IKeymasterDevice.hal
index 2664765..9bd8602 100644
--- a/keymaster/3.0/IKeymasterDevice.hal
+++ b/keymaster/3.0/IKeymasterDevice.hal
@@ -20,6 +20,7 @@
* Keymaster device definition. For thorough documentation see the implementer's reference, at
* https://source.android.com/security/keystore/implementer-ref.html
*/
+@SensitiveData
interface IKeymasterDevice {
/**
diff --git a/keymaster/4.0/IKeymasterDevice.hal b/keymaster/4.0/IKeymasterDevice.hal
index 3475f79..dfde060 100644
--- a/keymaster/4.0/IKeymasterDevice.hal
+++ b/keymaster/4.0/IKeymasterDevice.hal
@@ -195,7 +195,7 @@
* Tag::VENDOR_PATCHLEVEL, and Tag::BOOT_PATCHLEVEL must be cryptographically bound to every
* IKeymasterDevice key, as described in the Key Access Control section above.
*/
-
+@SensitiveData
interface IKeymasterDevice {
/**
diff --git a/keymaster/4.1/IKeymasterDevice.hal b/keymaster/4.1/IKeymasterDevice.hal
index bbeccaa..ccb9f2e 100644
--- a/keymaster/4.1/IKeymasterDevice.hal
+++ b/keymaster/4.1/IKeymasterDevice.hal
@@ -37,6 +37,7 @@
* versions will be numbered as major_version * 10 + minor version. The addition of new attestable
* tags changes the attestation format again, slightly, so the attestationVersion must be 4.
*/
+@SensitiveData
interface IKeymasterDevice extends @4.0::IKeymasterDevice {
/**
* Called by client to notify the IKeymasterDevice that the device is now locked, and keys with
diff --git a/keymaster/4.1/support/attestation_record.cpp b/keymaster/4.1/support/attestation_record.cpp
index 598b6b5..207a7e8 100644
--- a/keymaster/4.1/support/attestation_record.cpp
+++ b/keymaster/4.1/support/attestation_record.cpp
@@ -102,6 +102,7 @@
ASN1_INTEGER* boot_patchlevel;
ASN1_NULL* early_boot_only;
ASN1_NULL* device_unique_attestation;
+ ASN1_NULL* identity_credential_key;
} KM_AUTH_LIST;
ASN1_SEQUENCE(KM_AUTH_LIST) = {
@@ -145,6 +146,8 @@
ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
ASN1_EXP_OPT(KM_AUTH_LIST, device_unique_attestation, ASN1_NULL,
TAG_DEVICE_UNIQUE_ATTESTATION.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, identity_credential_key, ASN1_NULL,
+ TAG_IDENTITY_CREDENTIAL_KEY.maskedTag()),
} ASN1_SEQUENCE_END(KM_AUTH_LIST);
IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
@@ -285,6 +288,7 @@
copyAuthTag(record->unlocked_device_required, TAG_UNLOCKED_DEVICE_REQUIRED, auth_list);
copyAuthTag(record->early_boot_only, TAG_EARLY_BOOT_ONLY, auth_list);
copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
+ copyAuthTag(record->identity_credential_key, TAG_IDENTITY_CREDENTIAL_KEY, auth_list);
return ErrorCode::OK;
}
@@ -327,7 +331,10 @@
p = attest_rec->data;
KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, attest_rec->length));
- if (!record.get()) return {ErrorCode::UNKNOWN_ERROR, {}};
+ if (!record.get()) {
+ LOG(ERROR) << "Unable to get key description";
+ return {ErrorCode::UNKNOWN_ERROR, {}};
+ }
AttestationRecord result;
@@ -352,10 +359,12 @@
if (error != ErrorCode::OK) return {error, {}};
KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+ SecurityLevel root_of_trust_security_level = SecurityLevel::TRUSTED_ENVIRONMENT;
if (record->tee_enforced && record->tee_enforced->root_of_trust) {
root_of_trust = record->tee_enforced->root_of_trust;
} else if (record->software_enforced && record->software_enforced->root_of_trust) {
root_of_trust = record->software_enforced->root_of_trust;
+ root_of_trust_security_level = SecurityLevel::SOFTWARE;
} else {
LOG(ERROR) << AT << " Failed root of trust parsing";
return {ErrorCode::INVALID_ARGUMENT, {}};
@@ -373,6 +382,7 @@
rot.verified_boot_state = static_cast<keymaster_verified_boot_t>(
ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
rot.device_locked = root_of_trust->device_locked;
+ rot.security_level = root_of_trust_security_level;
auto& vb_hash = root_of_trust->verified_boot_hash;
if (!vb_hash) {
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
new file mode 100644
index 0000000..8c04b88
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.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_UTILS_COMMON_INVALID_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BUFFER_H
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidBuffer final : public nn::IBuffer {
+ public:
+ nn::Request::MemoryDomainToken getToken() const override;
+
+ nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+
+ nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+ const nn::Dimensions& dimensions) const override;
+};
+
+} // namespace android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BUFFER_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
new file mode 100644
index 0000000..5e62b9a
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
@@ -0,0 +1,80 @@
+/*
+ * 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_INVALID_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_DEVICE_H
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidDevice final : public nn::IDevice {
+ public:
+ InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
+ nn::DeviceType type, std::vector<nn::Extension> extensions,
+ nn::Capabilities capabilities,
+ std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded);
+
+ const std::string& getName() const override;
+ const std::string& getVersionString() const override;
+ nn::Version getFeatureLevel() const override;
+ nn::DeviceType getType() const override;
+ const std::vector<nn::Extension>& getSupportedExtensions() const override;
+ const nn::Capabilities& getCapabilities() const override;
+ std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+ nn::GeneralResult<void> wait() const override;
+
+ nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+ const nn::Model& model) const override;
+
+ nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+ const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+ nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+ const std::vector<nn::SharedHandle>& dataCache,
+ const nn::CacheToken& token) const override;
+
+ nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+ nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+ const std::vector<nn::SharedHandle>& dataCache,
+ const nn::CacheToken& token) const override;
+
+ nn::GeneralResult<nn::SharedBuffer> allocate(
+ const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+ const std::vector<nn::BufferRole>& inputRoles,
+ const std::vector<nn::BufferRole>& outputRoles) const override;
+
+ private:
+ const std::string kName;
+ const std::string kVersionString;
+ const nn::Version kFeatureLevel;
+ const nn::DeviceType kType;
+ const std::vector<nn::Extension> kExtensions;
+ const nn::Capabilities kCapabilities;
+ const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+};
+
+} // namespace android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_DEVICE_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
new file mode 100644
index 0000000..4b32b4e
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
@@ -0,0 +1,48 @@
+/*
+ * 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_INVALID_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_PREPARED_MODEL_H
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidPreparedModel final : public nn::IPreparedModel {
+ public:
+ 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;
+
+ 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;
+
+ std::any getUnderlyingResource() const override;
+};
+
+} // namespace android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_PREPARED_MODEL_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
index 4a84e4d..4bfed6c 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
@@ -45,8 +45,9 @@
std::string versionString, std::vector<nn::Extension> extensions,
nn::Capabilities capabilities, nn::SharedDevice device);
- nn::SharedDevice getDevice() const;
- nn::SharedDevice recover(const nn::IDevice* failingDevice, bool blocking) const;
+ nn::SharedDevice getDevice() const EXCLUDES(mMutex);
+ nn::SharedDevice recover(const nn::IDevice* failingDevice, bool blocking) const
+ EXCLUDES(mMutex);
const std::string& getName() const override;
const std::string& getVersionString() const override;
@@ -78,6 +79,7 @@
const std::vector<nn::BufferRole>& outputRoles) const override;
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,
@@ -100,6 +102,7 @@
const nn::Capabilities kCapabilities;
mutable std::mutex mMutex;
mutable nn::SharedDevice mDevice GUARDED_BY(mMutex);
+ mutable bool mIsValid GUARDED_BY(mMutex) = true;
};
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidBuffer.cpp b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
new file mode 100644
index 0000000..c6f75d7
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidBuffer.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 "InvalidBuffer.h"
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::Request::MemoryDomainToken InvalidBuffer::getToken() const {
+ return nn::Request::MemoryDomainToken{};
+}
+
+nn::GeneralResult<void> InvalidBuffer::copyTo(const nn::Memory& /*dst*/) const {
+ return NN_ERROR() << "InvalidBuffer";
+}
+
+nn::GeneralResult<void> InvalidBuffer::copyFrom(const nn::Memory& /*src*/,
+ const nn::Dimensions& /*dimensions*/) const {
+ return NN_ERROR() << "InvalidBuffer";
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidDevice.cpp b/neuralnetworks/utils/common/src/InvalidDevice.cpp
new file mode 100644
index 0000000..535ccb4
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidDevice.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "InvalidDevice.h"
+
+#include "InvalidBuffer.h"
+#include "InvalidPreparedModel.h"
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+InvalidDevice::InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
+ nn::DeviceType type, std::vector<nn::Extension> extensions,
+ nn::Capabilities capabilities,
+ std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded)
+ : kName(std::move(name)),
+ kVersionString(std::move(versionString)),
+ kFeatureLevel(featureLevel),
+ kType(type),
+ kExtensions(std::move(extensions)),
+ kCapabilities(std::move(capabilities)),
+ kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded) {}
+
+const std::string& InvalidDevice::getName() const {
+ return kName;
+}
+
+const std::string& InvalidDevice::getVersionString() const {
+ return kVersionString;
+}
+
+nn::Version InvalidDevice::getFeatureLevel() const {
+ return kFeatureLevel;
+}
+
+nn::DeviceType InvalidDevice::getType() const {
+ return kType;
+}
+
+const std::vector<nn::Extension>& InvalidDevice::getSupportedExtensions() const {
+ return kExtensions;
+}
+
+const nn::Capabilities& InvalidDevice::getCapabilities() const {
+ return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> InvalidDevice::getNumberOfCacheFilesNeeded() const {
+ return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> InvalidDevice::wait() const {
+ return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<std::vector<bool>> InvalidDevice::getSupportedOperations(
+ const nn::Model& /*model*/) const {
+ return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> InvalidDevice::prepareModel(
+ const nn::Model& /*model*/, nn::ExecutionPreference /*preference*/,
+ nn::Priority /*priority*/, nn::OptionalTimePoint /*deadline*/,
+ const std::vector<nn::SharedHandle>& /*modelCache*/,
+ const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+ return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> InvalidDevice::prepareModelFromCache(
+ nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+ const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+ return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedBuffer> InvalidDevice::allocate(
+ const nn::BufferDesc& /*desc*/,
+ const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+ const std::vector<nn::BufferRole>& /*inputRoles*/,
+ const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+ return NN_ERROR() << "InvalidDevice";
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
new file mode 100644
index 0000000..9ae7a63
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "InvalidPreparedModel.h"
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+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 {
+ return NN_ERROR() << "InvalidPreparedModel";
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+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 {
+ return NN_ERROR() << "InvalidPreparedModel";
+}
+
+std::any InvalidPreparedModel::getUnderlyingResource() const {
+ return {};
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientDevice.cpp b/neuralnetworks/utils/common/src/ResilientDevice.cpp
index 26025a5..2f83c5c 100644
--- a/neuralnetworks/utils/common/src/ResilientDevice.cpp
+++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp
@@ -16,6 +16,9 @@
#include "ResilientDevice.h"
+#include "InvalidBuffer.h"
+#include "InvalidDevice.h"
+#include "InvalidPreparedModel.h"
#include "ResilientBuffer.h"
#include "ResilientPreparedModel.h"
@@ -107,12 +110,21 @@
}
auto device = std::move(maybeDevice).value();
- // TODO(b/173081926): Instead of CHECKing to ensure the cache has not been changed, return an
- // invalid/"null" IDevice object that always fails.
- CHECK_EQ(kName, device->getName());
- CHECK_EQ(kVersionString, device->getVersionString());
- CHECK(kExtensions == device->getSupportedExtensions());
- CHECK_EQ(kCapabilities, device->getCapabilities());
+ // 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.
+ auto compare = [this, &device](auto fn) REQUIRES(mMutex) {
+ return std::invoke(fn, mDevice) != std::invoke(fn, device);
+ };
+ if (compare(&IDevice::getName) || compare(&IDevice::getVersionString) ||
+ compare(&IDevice::getFeatureLevel) || compare(&IDevice::getType) ||
+ compare(&IDevice::getSupportedExtensions) || compare(&IDevice::getCapabilities)) {
+ LOG(ERROR) << "Recovered device has different metadata than what is cached. Marking "
+ "IDevice object as invalid.";
+ device = std::make_shared<const InvalidDevice>(
+ kName, kVersionString, mDevice->getFeatureLevel(), mDevice->getType(), kExtensions,
+ kCapabilities, mDevice->getNumberOfCacheFilesNeeded());
+ mIsValid = false;
+ }
mDevice = std::move(device);
return mDevice;
@@ -199,11 +211,19 @@
return ResilientBuffer::create(std::move(makeBuffer));
}
+bool ResilientDevice::isValidInternal() const {
+ std::lock_guard hold(mMutex);
+ return mIsValid;
+}
+
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 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) {
return device.prepareModel(model, preference, priority, deadline, modelCache, dataCache,
@@ -216,6 +236,9 @@
bool blocking, 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) {
return device.prepareModelFromCache(deadline, modelCache, dataCache, token);
};
@@ -227,6 +250,9 @@
const std::vector<nn::SharedPreparedModel>& preparedModels,
const std::vector<nn::BufferRole>& inputRoles,
const std::vector<nn::BufferRole>& outputRoles) const {
+ if (!isValidInternal()) {
+ return std::make_shared<const InvalidBuffer>();
+ }
const auto fn = [&desc, &preparedModels, &inputRoles, &outputRoles](const nn::IDevice& device) {
return device.allocate(desc, preparedModels, inputRoles, outputRoles);
};
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
index 8afbf22..c73745a 100644
--- a/radio/1.6/IRadio.hal
+++ b/radio/1.6/IRadio.hal
@@ -340,13 +340,22 @@
*
* @param serial Serial number of request.
* @param dataThrottlingAction DataThrottlingAction as defined in types.hal
- * @param completionWindowSecs window, in seconds, in which the requested
- * throttling action has to be achieved. This must be 0 when
+ * @param completionDurationMillis window, in milliseconds, in which the
+ * requested throttling action has to be achieved. This must be 0 when
* dataThrottlingAction is DataThrottlingAction:HOLD.
*
* Response function is IRadioResponse.setDataThrottlingResponse()
*/
oneway setDataThrottling(int32_t serial,
DataThrottlingAction dataThrottlingAction,
- int32_t completionWindowSecs);
+ int64_t completionDurationMillis);
+
+ /**
+ * Get which bands the modem's background scan is acting on.
+ *
+ * @param serial Serial number of request.
+ *
+ * Response callback is IRadioResponse.getSystemSelectionChannelsResponse()
+ */
+ oneway getSystemSelectionChannels(int32_t serial);
};
diff --git a/radio/1.6/IRadioResponse.hal b/radio/1.6/IRadioResponse.hal
index 5a71c1f..c545db0 100644
--- a/radio/1.6/IRadioResponse.hal
+++ b/radio/1.6/IRadioResponse.hal
@@ -317,4 +317,15 @@
* RadioError:INVALID_ARGUMENTS
*/
oneway setDataThrottlingResponse(RadioResponseInfo info);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ * RadioError:INVALID_ARGUMENTS
+ */
+ oneway getSystemSelectionChannelsResponse(RadioResponseInfo info);
};
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 d3ffba9..6b1ff27 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -303,7 +303,7 @@
serial = GetRandomSerialNumber();
Return<void> res = radio_v1_6->setDataThrottling(
- serial, DataThrottlingAction::THROTTLE_SECONDARY_CARRIER, 60);
+ serial, DataThrottlingAction::THROTTLE_SECONDARY_CARRIER, 60000);
ASSERT_OK(res);
EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -318,7 +318,8 @@
serial = GetRandomSerialNumber();
- res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER, 60);
+ res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER,
+ 60000);
ASSERT_OK(res);
EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -333,7 +334,7 @@
serial = GetRandomSerialNumber();
- res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::HOLD, 60);
+ res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::HOLD, 60000);
ASSERT_OK(res);
EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -348,7 +349,7 @@
serial = GetRandomSerialNumber();
- res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60);
+ res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60000);
ASSERT_OK(res);
EXPECT_EQ(std::cv_status::no_timeout, wait());
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 fcf679c..85be903 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
@@ -795,6 +795,9 @@
Return<void> setDataThrottlingResponse(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+ Return<void> getSystemSelectionChannelsResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
};
/* 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 788038a..7da675e 100644
--- a/radio/1.6/vts/functional/radio_response.cpp
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -1163,3 +1163,10 @@
parent_v1_6.notify(info.serial);
return Void();
}
+
+Return<void> RadioResponse_v1_6::getSystemSelectionChannelsResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+ rspInfo = info;
+ parent_v1_6.notify(info.serial);
+ return Void();
+}
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
new file mode 100644
index 0000000..ace0de9
--- /dev/null
+++ b/radio/config/1.3/Android.bp
@@ -0,0 +1,21 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.radio.config@1.3",
+ root: "android.hardware",
+ srcs: [
+ "types.hal",
+ "IRadioConfig.hal",
+ "IRadioConfigResponse.hal",
+ ],
+ interfaces: [
+ "android.hardware.radio.config@1.0",
+ "android.hardware.radio.config@1.1",
+ "android.hardware.radio.config@1.2",
+ "android.hardware.radio@1.0",
+ "android.hardware.radio@1.6",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+ system_ext_specific: true,
+}
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
new file mode 100644
index 0000000..83bcf92
--- /dev/null
+++ b/radio/config/1.3/IRadioConfig.hal
@@ -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.
+ *
+ *
+ * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
+ * radio configuration, and it is not associated with any specific modem or slot.
+ * All the functions have minimum one parameter:
+ * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
+ * duration of a method call. If clients provide colliding serials (including passing the same
+ * serial to different methods), multiple responses (one for each method call) must still be served.
+ */
+
+package android.hardware.radio.config@1.3;
+
+import @1.1::IRadioConfig;
+import IRadioConfigResponse;
+
+interface IRadioConfig extends @1.1::IRadioConfig {
+ /**
+ * Gets the available Radio Hal capabilities on the current device.
+ *
+ * This is called once per device boot up.
+ *
+ * @param serial Serial number of request
+ *
+ * Response callback is
+ * IRadioConfigResponse.getHalDeviceCapabilitiesResponse()
+ */
+ oneway getHalDeviceCapabilities(int32_t serial);
+};
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
new file mode 100644
index 0000000..863754f
--- /dev/null
+++ b/radio/config/1.3/IRadioConfigResponse.hal
@@ -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.
+ */
+
+package android.hardware.radio.config@1.3;
+
+import android.hardware.radio@1.6::RadioResponseInfo;
+import @1.2::IRadioConfigResponse;
+import HalDeviceCapabilities;
+
+/**
+ * Interface declaring response functions to solicited radio config requests.
+ */
+interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ * @param capabilities Capabilities struct containing the capabilities of the
+ * device related to the Radio HAL
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ */
+ oneway getHalDeviceCapabilitiesResponse(RadioResponseInfo info,
+ HalDeviceCapabilities capabilities);
+};
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
new file mode 100644
index 0000000..bedb709
--- /dev/null
+++ b/radio/config/1.3/types.hal
@@ -0,0 +1,22 @@
+/*
+ * 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.radio.config@1.3;
+
+/**
+ * Contains the device capabilities with respect to the Radio HAL.
+ */
+struct HalDeviceCapabilities {};
diff --git a/radio/config/1.3/vts/functional/Android.bp b/radio/config/1.3/vts/functional/Android.bp
new file mode 100644
index 0000000..abd081f
--- /dev/null
+++ b/radio/config/1.3/vts/functional/Android.bp
@@ -0,0 +1,39 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "VtsHalRadioConfigV1_3TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "radio_config_hidl_hal_api.cpp",
+ "radio_config_hidl_hal_test.cpp",
+ "radio_config_response.cpp",
+ "radio_config_indication.cpp",
+ "VtsHalRadioConfigV1_3TargetTest.cpp",
+ ],
+ static_libs: [
+ "RadioVtsTestUtilBase",
+ "android.hardware.radio.config@1.0",
+ "android.hardware.radio.config@1.1",
+ "android.hardware.radio.config@1.2",
+ "android.hardware.radio.config@1.3",
+ ],
+ header_libs: ["radio.util.header@1.0"],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
new file mode 100644
index 0000000..5772d08
--- /dev/null
+++ b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioConfigHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, RadioConfigHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
new file mode 100644
index 0000000..8df02dd
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
@@ -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.
+ */
+
+#include <radio_config_hidl_hal_utils.h>
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadioConfig.getHalDeviceCapabilities()
+ */
+TEST_P(RadioConfigHidlTest, getHalDeviceCapabilities) {
+ const int serial = GetRandomSerialNumber();
+ Return<void> res = radioConfig->getHalDeviceCapabilities(serial);
+ ASSERT_OK(res);
+ ALOGI("getHalDeviceCapabilities, rspInfo.error = %s\n",
+ toString(radioConfigRsp->rspInfo.error).c_str());
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
new file mode 100644
index 0000000..de8365a
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+void RadioConfigHidlTest::SetUp() {
+ radioConfig = IRadioConfig::getService(GetParam());
+ if (radioConfig == NULL) {
+ sleep(60);
+ radioConfig = IRadioConfig::getService(GetParam());
+ }
+ ASSERT_NE(nullptr, radioConfig.get());
+
+ radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
+ ASSERT_NE(nullptr, radioConfigRsp.get());
+
+ count_ = 0;
+
+ radioConfig->setResponseFunctions(radioConfigRsp, nullptr);
+}
+
+/*
+ * Notify that the response message is received.
+ */
+void RadioConfigHidlTest::notify(int receivedSerial) {
+ std::unique_lock<std::mutex> lock(mtx_);
+ if (serial == receivedSerial) {
+ count_++;
+ cv_.notify_one();
+ }
+}
+
+/*
+ * Wait till the response message is notified or till TIMEOUT_PERIOD.
+ */
+std::cv_status RadioConfigHidlTest::wait() {
+ std::unique_lock<std::mutex> lock(mtx_);
+
+ std::cv_status status = std::cv_status::no_timeout;
+ auto now = std::chrono::system_clock::now();
+ while (count_ == 0) {
+ status = cv_.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+ if (status == std::cv_status::timeout) {
+ return status;
+ }
+ }
+ count_--;
+ return status;
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
new file mode 100644
index 0000000..439eb70
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
@@ -0,0 +1,135 @@
+/*
+ * 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 <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/config/1.1/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/types.h>
+#include <android/hardware/radio/config/1.2/IRadioConfigIndication.h>
+#include <android/hardware/radio/config/1.2/IRadioConfigResponse.h>
+#include <android/hardware/radio/config/1.2/types.h>
+#include <android/hardware/radio/config/1.3/IRadioConfig.h>
+#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
+#include <android/hardware/radio/config/1.3/types.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include "vts_test_util.h"
+
+using namespace ::android::hardware::radio::config::V1_2;
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::radio::config::V1_1::ModemsConfig;
+using ::android::hardware::radio::config::V1_1::PhoneCapability;
+using ::android::hardware::radio::config::V1_2::SimSlotStatus;
+using ::android::hardware::radio::config::V1_3::HalDeviceCapabilities;
+using ::android::hardware::radio::config::V1_3::IRadioConfig;
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
+
+#define TIMEOUT_PERIOD 75
+#define RADIO_SERVICE_NAME "slot1"
+
+class RadioConfigHidlTest;
+
+/* Callback class for radio config response */
+class RadioConfigResponse : public IRadioConfigResponse {
+ protected:
+ RadioConfigHidlTest& parent;
+
+ public:
+ RadioResponseInfo rspInfo;
+ PhoneCapability phoneCap;
+
+ RadioConfigResponse(RadioConfigHidlTest& parent);
+ virtual ~RadioConfigResponse() = default;
+
+ Return<void> getSimSlotsStatusResponse(
+ const RadioResponseInfo& info,
+ const ::android::hardware::hidl_vec<
+ ::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus);
+
+ Return<void> getSimSlotsStatusResponse_1_2(
+ const RadioResponseInfo& info,
+ const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+
+ Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
+
+ Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
+ const PhoneCapability& phoneCapability);
+
+ Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
+
+ Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
+ const ModemsConfig& mConfig);
+
+ Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
+
+ Return<void> getHalDeviceCapabilitiesResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const HalDeviceCapabilities& halDeviceCapabilities);
+};
+
+/* Callback class for radio config indication */
+class RadioConfigIndication : public IRadioConfigIndication {
+ protected:
+ RadioConfigHidlTest& parent;
+
+ public:
+ RadioConfigIndication(RadioConfigHidlTest& parent);
+ virtual ~RadioConfigIndication() = default;
+
+ Return<void> simSlotsStatusChanged_1_2(
+ ::android::hardware::radio::V1_0::RadioIndicationType type,
+ const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+};
+
+// The main test class for Radio config HIDL.
+class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
+ protected:
+ std::mutex mtx_;
+ std::condition_variable cv_;
+ int count_;
+
+ public:
+ virtual void SetUp() override;
+
+ /* Used as a mechanism to inform the test about data/event callback */
+ void notify(int receivedSerial);
+
+ /* Test code calls this function to wait for response */
+ std::cv_status wait();
+
+ void updateSimCardStatus();
+
+ /* Serial number for radio request */
+ int serial;
+
+ /* radio config service handle */
+ sp<IRadioConfig> radioConfig;
+
+ /* radio config response handle */
+ sp<RadioConfigResponse> radioConfigRsp;
+};
diff --git a/radio/config/1.3/vts/functional/radio_config_indication.cpp b/radio/config/1.3/vts/functional/radio_config_indication.cpp
new file mode 100644
index 0000000..6fa443c
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_indication.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+RadioConfigIndication::RadioConfigIndication(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigIndication::simSlotsStatusChanged_1_2(
+ ::android::hardware::radio::V1_0::RadioIndicationType /*type*/,
+ const ::android::hardware::hidl_vec<SimSlotStatus>& /*slotStatus*/) {
+ return Void();
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
new file mode 100644
index 0000000..2a8b28b
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+// SimSlotStatus slotStatus;
+
+RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+ const ::android::hardware::hidl_vec<
+ ::android::hardware::radio::config::V1_0::SimSlotStatus>& /* slotStatus */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+ const ::android::hardware::hidl_vec<SimSlotStatus>& /* slotStatus */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::setSimSlotsMappingResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+ const PhoneCapability& phoneCapability) {
+ rspInfo = info;
+ phoneCap = phoneCapability;
+ parent.notify(info.serial);
+ return Void();
+}
+
+Return<void> RadioConfigResponse::setPreferredDataModemResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::getModemsConfigResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+ const ModemsConfig& /* mConfig */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::setModemsConfigResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::getHalDeviceCapabilitiesResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& /* info */,
+ const ::android::hardware::radio::config::V1_3::HalDeviceCapabilities& /* capabilities */) {
+ return Void();
+}
\ No newline at end of file
diff --git a/tests/foo/1.0/IFoo.hal b/tests/foo/1.0/IFoo.hal
index 4c54427..e242616 100644
--- a/tests/foo/1.0/IFoo.hal
+++ b/tests/foo/1.0/IFoo.hal
@@ -21,6 +21,7 @@
import ISimple;
import ITheirTypes.FloatArray;
+@SensitiveData // for test
interface IFoo {
enum SomeBaseEnum : uint8_t {
diff --git a/tv/tuner/1.1/IFilter.hal b/tv/tuner/1.1/IFilter.hal
index 1c6c33f..1e94114 100644
--- a/tv/tuner/1.1/IFilter.hal
+++ b/tv/tuner/1.1/IFilter.hal
@@ -85,19 +85,23 @@
configureAvStreamType(AvStreamType avStreamType) generates (Result result);
/**
- * Configure the filter to monitor specific Scrambling Status.
+ * Configure the monitor event.
*
- * Scrambling Status should be sent through the filer callback at the following two scenarios:
+ * The event for Scrambling Status should be sent at the following two scenarios:
* 1. When this method is called, the first detected scrambling status should be sent.
- * 2. When the filter transits into the monitored statuses configured through this method,
- * event should be sent.
+ * 2. When the Scrambling status transits into different status, event should be sent.
*
- * @param statuses Scrambling Statuses to monitor. Set corresponding bit to monitor. Reset to
- * stop monitoring.
+ * The event for IP CID change should be sent at the following two scenarios:
+ * 1. When this method is called, the first detected CID for the IP should be sent.
+ * 2. When the CID is changed to different value for the IP filter, event should be sent.
+ *
+ * @param monitorEventypes the events to monitor. Set corresponding bit of the event to monitor.
+ * Reset to stop monitoring.
* @return result Result status of the operation.
- * SUCCESS if successful,
- * INVALID_STATE if configure can't be applied,
- * UNKNOWN_ERROR if failed for other reasons.
+ * SUCCESS if successful,
+ * INVALID_STATE if configure can't be applied,
+ * UNKNOWN_ERROR if failed for other reasons.
*/
- configureScramblingEvent(bitfield<ScramblingStatus> statuses) generates (Result result);
+ configureMonitorEvent(bitfield<DemuxFilterMonitorEventType> monitorEventTypes)
+ generates (Result result);
};
diff --git a/tv/tuner/1.1/default/Filter.cpp b/tv/tuner/1.1/default/Filter.cpp
index e766a8a..4fa1746 100644
--- a/tv/tuner/1.1/default/Filter.cpp
+++ b/tv/tuner/1.1/default/Filter.cpp
@@ -250,25 +250,49 @@
return Result::SUCCESS;
}
-Return<Result> Filter::configureScramblingEvent(uint32_t statuses) {
+Return<Result> Filter::configureMonitorEvent(uint32_t monitorEventTypes) {
ALOGV("%s", __FUNCTION__);
- mStatuses = statuses;
- if (mCallback_1_1 != nullptr) {
- // Assuming current status is always NOT_SCRAMBLED
- V1_1::DemuxFilterEventExt filterEventExt;
- V1_1::DemuxFilterEventExt::Event event;
- event.scramblingStatus(V1_1::ScramblingStatus::NOT_SCRAMBLED);
- int size = filterEventExt.events.size();
- filterEventExt.events.resize(size + 1);
- filterEventExt.events[size] = event;
- DemuxFilterEvent emptyFilterEvent;
+ DemuxFilterEvent emptyFilterEvent;
+ V1_1::DemuxFilterMonitorEvent monitorEvent;
+ V1_1::DemuxFilterEventExt eventExt;
- mCallback_1_1->onFilterEvent_1_1(emptyFilterEvent, filterEventExt);
- mFilterEventExt.events.resize(0);
- } else {
- return Result::INVALID_STATE;
+ uint32_t newScramblingStatus =
+ monitorEventTypes & V1_1::DemuxFilterMonitorEventType::SCRAMBLING_STATUS;
+ uint32_t newIpCid = monitorEventTypes & V1_1::DemuxFilterMonitorEventType::IP_CID_CHANGE;
+
+ // if scrambling status monitoring flipped, record the new state and send msg on enabling
+ if (newScramblingStatus ^ mScramblingStatusMonitored) {
+ mScramblingStatusMonitored = newScramblingStatus;
+ if (mScramblingStatusMonitored) {
+ if (mCallback_1_1 != nullptr) {
+ // Assuming current status is always NOT_SCRAMBLED
+ monitorEvent.scramblingStatus(V1_1::ScramblingStatus::NOT_SCRAMBLED);
+ eventExt.events.resize(1);
+ eventExt.events[0].monitorEvent(monitorEvent);
+ mCallback_1_1->onFilterEvent_1_1(emptyFilterEvent, eventExt);
+ } else {
+ return Result::INVALID_STATE;
+ }
+ }
}
+
+ // if ip cid monitoring flipped, record the new state and send msg on enabling
+ if (newIpCid ^ mIpCidMonitored) {
+ mIpCidMonitored = newIpCid;
+ if (mIpCidMonitored) {
+ if (mCallback_1_1 != nullptr) {
+ // Return random cid
+ monitorEvent.cid(1);
+ eventExt.events.resize(1);
+ eventExt.events[0].monitorEvent(monitorEvent);
+ mCallback_1_1->onFilterEvent_1_1(emptyFilterEvent, eventExt);
+ } else {
+ return Result::INVALID_STATE;
+ }
+ }
+ }
+
return Result::SUCCESS;
}
diff --git a/tv/tuner/1.1/default/Filter.h b/tv/tuner/1.1/default/Filter.h
index 1ebc135..3a4246e 100644
--- a/tv/tuner/1.1/default/Filter.h
+++ b/tv/tuner/1.1/default/Filter.h
@@ -84,7 +84,7 @@
virtual Return<Result> configureAvStreamType(const V1_1::AvStreamType& avStreamType) override;
- virtual Return<Result> configureScramblingEvent(uint32_t statuses) override;
+ virtual Return<Result> configureMonitorEvent(uint32_t monitorEventTypes) override;
/**
* To create a FilterMQ and its Event Flag.
@@ -238,6 +238,8 @@
bool mConfigured = false;
int mStartId = 0;
+ uint8_t mScramblingStatusMonitored = 0;
+ uint8_t mIpCidMonitored = 0;
};
} // namespace implementation
diff --git a/tv/tuner/1.1/types.hal b/tv/tuner/1.1/types.hal
index 09b87f2..938cb6e 100644
--- a/tv/tuner/1.1/types.hal
+++ b/tv/tuner/1.1/types.hal
@@ -151,22 +151,42 @@
struct DemuxFilterEventExt {
safe_union Event {
/**
- * No extended record filter Event. This is used by the tsRecord or mmtpRecord filter event
- * that does not contain the DemuxFilterTs/MmtpRecordEventExt information.
+ * No extended filter Event.
*/
Monostate noinit;
+ /**
+ * Extended TS Record event sent along with @1.0::DemuxFilterEvent::Event::tsRecord.
+ * DemuxFilterEventExt.events[i] is corresponding to @1.0::DemuxFilterEvent.events[i]. If
+ * @1.0::DemuxFilterEvent.events[i] does not have extended event,
+ * DemuxFilterEventExt.events[i] should use Monostate.
+ */
DemuxFilterTsRecordEventExt tsRecord;
+ /**
+ * Extended MMTP Record event sent along with @1.0::DemuxFilterEvent::Event::mmtpRecord.
+ * DemuxFilterEventExt.events[i] is corresponding to @1.0::DemuxFilterEvent.events[i]. If
+ * @1.0::DemuxFilterEvent.events[i] does not have extended event,
+ * DemuxFilterEventExt.events[i] should use Monostate.
+ */
DemuxFilterMmtpRecordEventExt mmtpRecord;
- ScramblingStatus scramblingStatus;
+ /**
+ * Monitor event to notify monitored status change.
+ *
+ * When sending monitorEvent, DemuxFilterEventExt.events should only contain one
+ * monitorEvent. MonitorEvent should be sent with an empty @1.0::DemuxFilterEvent.
+ */
+ DemuxFilterMonitorEvent monitorEvent;
/**
* An unique ID to mark the start point of receiving the valid filter events after
* reconfiguring the filter. It must be sent at least once in the first event after the
* filter is restarted. 0 is reserved for the newly opened filter's first start, which is
* optional for HAL to send.
+ *
+ * When sending starId, DemuxFilterEventExt.events should only contain one startId event.
+ * StardId event should be sent with an empty @1.0::DemuxFilterEvent.
*/
uint32_t startId;
};
@@ -196,6 +216,24 @@
SCRAMBLED = 1 << 2,
};
+@export
+enum DemuxFilterMonitorEventType : uint32_t {
+ SCRAMBLING_STATUS = 1 << 0,
+ IP_CID_CHANGE = 1 << 1,
+};
+
+safe_union DemuxFilterMonitorEvent {
+ /**
+ * New scrambling status.
+ */
+ ScramblingStatus scramblingStatus;
+
+ /**
+ * New cid for the IP filter.
+ */
+ uint32_t cid;
+};
+
typedef FrontendDvbcSpectralInversion FrontendSpectralInversion;
/**
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.cpp b/tv/tuner/1.1/vts/functional/FilterTests.cpp
index cdf3b69..d8fad3d 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.1/vts/functional/FilterTests.cpp
@@ -40,6 +40,18 @@
ALOGW("[vts] pass and stop");
}
+void FilterCallback::testFilterIpCidEvent() {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (mIpCidEvent < 1) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "ip cid change event does not output within timeout";
+ return;
+ }
+ }
+ mIpCidEvent = 0;
+ ALOGW("[vts] pass and stop");
+}
+
void FilterCallback::testStartIdAfterReconfigure() {
android::Mutex::Autolock autoLock(mMsgLock);
while (!mStartIdReceived) {
@@ -80,8 +92,17 @@
eventExt.mmtpRecord().pts, eventExt.mmtpRecord().firstMbInSlice,
eventExt.mmtpRecord().mpuSequenceNumber, eventExt.mmtpRecord().tsIndexMask);
break;
- case DemuxFilterEventExt::Event::hidl_discriminator::scramblingStatus:
- mScramblingStatusEvent++;
+ case DemuxFilterEventExt::Event::hidl_discriminator::monitorEvent:
+ switch (eventExt.monitorEvent().getDiscriminator()) {
+ case DemuxFilterMonitorEvent::hidl_discriminator::scramblingStatus:
+ mScramblingStatusEvent++;
+ break;
+ case DemuxFilterMonitorEvent::hidl_discriminator::cid:
+ mIpCidEvent++;
+ break;
+ default:
+ break;
+ }
break;
case DemuxFilterEventExt::Event::hidl_discriminator::startId:
ALOGD("[vts] Extended restart filter event, startId=%d", eventExt.startId());
@@ -272,15 +293,16 @@
return AssertionResult(status == Result::SUCCESS);
}
-AssertionResult FilterTests::configureScramblingEvent(uint64_t filterId, uint32_t statuses) {
+AssertionResult FilterTests::configureMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes) {
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
Result status;
sp<android::hardware::tv::tuner::V1_1::IFilter> filter_v1_1 =
android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilters[filterId]);
if (filter_v1_1 != NULL) {
- status = filter_v1_1->configureScramblingEvent(statuses);
+ status = filter_v1_1->configureMonitorEvent(monitorEventTypes);
mFilterCallbacks[filterId]->testFilterScramblingEvent();
+ mFilterCallbacks[filterId]->testFilterIpCidEvent();
} else {
ALOGW("[vts] Can't cast IFilter into v1_1.");
return failure();
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.h b/tv/tuner/1.1/vts/functional/FilterTests.h
index ae57659..6749265 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.h
+++ b/tv/tuner/1.1/vts/functional/FilterTests.h
@@ -56,6 +56,7 @@
using android::hardware::tv::tuner::V1_0::Result;
using android::hardware::tv::tuner::V1_1::AvStreamType;
using android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
+using android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
using android::hardware::tv::tuner::V1_1::IFilterCallback;
using android::hardware::tv::tuner::V1_1::ITuner;
@@ -118,6 +119,7 @@
void testFilterDataOutput();
void testFilterScramblingEvent();
+ void testFilterIpCidEvent();
void testStartIdAfterReconfigure();
void readFilterEventData();
@@ -139,6 +141,7 @@
int mPidFilterOutputCount = 0;
int mScramblingStatusEvent = 0;
+ int mIpCidEvent = 0;
bool mStartIdReceived = false;
};
@@ -157,7 +160,7 @@
AssertionResult configFilter(DemuxFilterSettings setting, uint64_t filterId);
AssertionResult configAvFilterStreamType(AvStreamType type, uint64_t filterId);
AssertionResult configIpFilterCid(uint32_t ipCid, uint64_t filterId);
- AssertionResult configureScramblingEvent(uint64_t filterId, uint32_t statuses);
+ AssertionResult configureMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes);
AssertionResult getFilterMQDescriptor(uint64_t filterId);
AssertionResult startFilter(uint64_t filterId);
AssertionResult stopFilter(uint64_t filterId);
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
index dda8b60..e872762 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
@@ -46,8 +46,8 @@
if (filterConf.type.mainType == DemuxFilterMainType::IP) {
ASSERT_TRUE(mFilterTests.configIpFilterCid(filterConf.ipCid, filterId));
}
- if (filterConf.statuses > 0) {
- ASSERT_TRUE(mFilterTests.configureScramblingEvent(filterId, filterConf.statuses));
+ if (filterConf.monitorEventTypes > 0) {
+ ASSERT_TRUE(mFilterTests.configureMonitorEvent(filterId, filterConf.monitorEventTypes));
}
ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
index 76bf765..beae223 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
@@ -96,7 +96,7 @@
DemuxFilterSettings settings;
AvStreamType streamType;
uint32_t ipCid;
- uint32_t statuses;
+ uint32_t monitorEventTypes;
bool operator<(const FilterConfig& /*c*/) const { return false; }
};
@@ -188,7 +188,9 @@
filterArray[TS_VIDEO0].bufferSize = FMQ_SIZE_16M;
filterArray[TS_VIDEO0].settings.ts().tpid = 256;
filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false});
- filterArray[TS_VIDEO0].statuses = 1;
+ filterArray[TS_VIDEO0].monitorEventTypes =
+ android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEventType::SCRAMBLING_STATUS |
+ android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEventType::IP_CID_CHANGE;
filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
filterArray[TS_VIDEO1].bufferSize = FMQ_SIZE_16M;