Merge "Merge android13-tests-dev"

This commit is contained in:
Xin Li
2022-08-23 23:07:05 +00:00
committed by Gerrit Code Review
16 changed files with 443 additions and 160 deletions

View File

@@ -14,7 +14,11 @@
* limitations under the License.
*/
#include <fstream>
#include <numeric>
#include <android-base/chrono_utils.h>
#include <cutils/properties.h>
#include "Generators.h"
@@ -517,20 +521,10 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
}
bool canQueryPresentationPosition() const {
auto maybeSinkAddress =
getCachedPolicyConfig().getSinkDeviceForMixPort(getDeviceName(), getMixPortName());
// Returning 'true' when no sink is found so the test can fail later with a more clear
// problem description.
return !maybeSinkAddress.has_value() ||
!xsd::isTelephonyDevice(maybeSinkAddress.value().deviceType);
return !xsd::isTelephonyDevice(address.deviceType);
}
void createPatchIfNeeded() {
auto maybeSinkAddress =
getCachedPolicyConfig().getSinkDeviceForMixPort(getDeviceName(), getMixPortName());
ASSERT_TRUE(maybeSinkAddress.has_value())
<< "No sink device found for mix port " << getMixPortName() << " (module "
<< getDeviceName() << ")";
if (areAudioPatchesSupported()) {
AudioPortConfig source;
source.base.format.value(getConfig().base.format);
@@ -540,13 +534,13 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
source.ext.mix().ioHandle = helper.getIoHandle();
source.ext.mix().useCase.stream({});
AudioPortConfig sink;
sink.ext.device(maybeSinkAddress.value());
sink.ext.device(address);
EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{source},
hidl_vec<AudioPortConfig>{sink},
returnIn(res, mPatchHandle)));
mHasPatch = res == Result::OK;
} else {
EXPECT_OK(stream->setDevices({maybeSinkAddress.value()}));
EXPECT_OK(stream->setDevices({address}));
}
}
@@ -556,10 +550,6 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
mHasPatch = false;
}
} else {
if (stream) {
EXPECT_OK(stream->setDevices({address}));
}
}
}
@@ -575,16 +565,22 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
// Sometimes HAL doesn't have enough information until the audio data actually gets
// consumed by the hardware.
bool timedOut = false;
res = Result::INVALID_STATE;
for (android::base::Timer elapsed;
res != Result::OK && !writer.hasError() &&
!(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
usleep(kWriteDurationUs);
ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts)));
ASSERT_RESULT(okOrInvalidState, res);
if (!firstPosition || *firstPosition == std::numeric_limits<uint64_t>::max()) {
res = Result::INVALID_STATE;
for (android::base::Timer elapsed;
res != Result::OK && !writer.hasError() &&
!(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
usleep(kWriteDurationUs);
ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts)));
ASSERT_RESULT(okOrInvalidState, res);
}
ASSERT_FALSE(writer.hasError());
ASSERT_FALSE(timedOut);
} else {
// Use `firstPosition` instead of querying it from the HAL. This is used when
// `waitForPresentationPositionAdvance` is called in a loop.
framesInitial = *firstPosition;
}
ASSERT_FALSE(writer.hasError());
ASSERT_FALSE(timedOut);
uint64_t frames = framesInitial;
for (android::base::Timer elapsed;
@@ -646,7 +642,7 @@ TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionPreservedOnStandby) {
ASSERT_OK(stream->standby());
writer.resume();
uint64_t frames;
uint64_t frames = std::numeric_limits<uint64_t>::max();
ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, &frames));
EXPECT_GT(frames, framesInitial);
@@ -691,24 +687,12 @@ class PcmOnlyConfigInputStreamTest : public InputStreamTest {
InputStreamTest::TearDown();
}
bool canQueryCapturePosition() const {
auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
getDeviceName(), getMixPortName());
// Returning 'true' when no source is found so the test can fail later with a more clear
// problem description.
return !maybeSourceAddress.has_value() ||
!xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType);
}
bool canQueryCapturePosition() const { return !xsd::isTelephonyDevice(address.deviceType); }
void createPatchIfNeeded() {
auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
getDeviceName(), getMixPortName());
ASSERT_TRUE(maybeSourceAddress.has_value())
<< "No source device found for mix port " << getMixPortName() << " (module "
<< getDeviceName() << ")";
if (areAudioPatchesSupported()) {
AudioPortConfig source;
source.ext.device(maybeSourceAddress.value());
source.ext.device(address);
AudioPortConfig sink;
sink.base.format.value(getConfig().base.format);
sink.base.sampleRateHz.value(getConfig().base.sampleRateHz);
@@ -721,7 +705,7 @@ class PcmOnlyConfigInputStreamTest : public InputStreamTest {
returnIn(res, mPatchHandle)));
mHasPatch = res == Result::OK;
} else {
EXPECT_OK(stream->setDevices({maybeSourceAddress.value()}));
EXPECT_OK(stream->setDevices({address}));
}
}
@@ -731,10 +715,6 @@ class PcmOnlyConfigInputStreamTest : public InputStreamTest {
EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
mHasPatch = false;
}
} else {
if (stream) {
EXPECT_OK(stream->setDevices({address}));
}
}
}
@@ -864,14 +844,8 @@ TEST_P(MicrophoneInfoInputStreamTest, GetActiveMicrophones) {
}
ASSERT_OK(res);
auto maybeSourceAddress =
getCachedPolicyConfig().getSourceDeviceForMixPort(getDeviceName(), getMixPortName());
ASSERT_TRUE(maybeSourceAddress.has_value())
<< "No source device found for mix port " << getMixPortName() << " (module "
<< getDeviceName() << ")";
for (auto microphone : microphones) {
if (microphone.deviceAddress == maybeSourceAddress.value()) {
if (microphone.deviceAddress == address) {
StreamReader reader(stream.get(), stream->getBufferSize());
ASSERT_TRUE(reader.start());
reader.pause(); // This ensures that at least one read has happened.
@@ -889,3 +863,176 @@ INSTANTIATE_TEST_CASE_P(MicrophoneInfoInputStream, MicrophoneInfoInputStreamTest
::testing::ValuesIn(getBuiltinMicConfigParameters()),
&DeviceConfigParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MicrophoneInfoInputStreamTest);
static const std::vector<DeviceConfigParameter>& getOutputDeviceCompressedConfigParameters(
const AudioConfigBase& configToMatch) {
static const std::vector<DeviceConfigParameter> parameters = [&] {
auto allParams = getOutputDeviceConfigParameters();
std::vector<DeviceConfigParameter> compressedParams;
std::copy_if(allParams.begin(), allParams.end(), std::back_inserter(compressedParams),
[&](auto cfg) {
if (std::get<PARAM_CONFIG>(cfg).base != configToMatch) return false;
const auto& flags = std::get<PARAM_FLAGS>(cfg);
return std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
return flag ==
toString(xsd::AudioInOutFlag::
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
}) != flags.end();
});
return compressedParams;
}();
return parameters;
}
class CompressedOffloadOutputStreamTest : public PcmOnlyConfigOutputStreamTest {
public:
void loadData(const std::string& fileName, std::vector<uint8_t>* data) {
std::ifstream is(fileName, std::ios::in | std::ios::binary);
ASSERT_TRUE(is.good()) << "Failed to open file " << fileName;
is.seekg(0, is.end);
data->reserve(data->size() + is.tellg());
is.seekg(0, is.beg);
data->insert(data->end(), std::istreambuf_iterator<char>(is),
std::istreambuf_iterator<char>());
ASSERT_TRUE(!is.fail()) << "Failed to read from file " << fileName;
}
};
class OffloadCallbacks : public IStreamOutCallback {
public:
Return<void> onDrainReady() override {
ALOGI("onDrainReady");
{
std::lock_guard lg(mLock);
mOnDrainReady = true;
}
mCondVar.notify_one();
return {};
}
Return<void> onWriteReady() override { return {}; }
Return<void> onError() override {
ALOGW("onError");
{
std::lock_guard lg(mLock);
mOnError = true;
}
mCondVar.notify_one();
return {};
}
bool waitForDrainReadyOrError() {
std::unique_lock l(mLock);
if (!mOnDrainReady && !mOnError) {
mCondVar.wait(l, [&]() { return mOnDrainReady || mOnError; });
}
const bool success = !mOnError;
mOnDrainReady = mOnError = false;
return success;
}
private:
std::mutex mLock;
bool mOnDrainReady = false;
bool mOnError = false;
std::condition_variable mCondVar;
};
TEST_P(CompressedOffloadOutputStreamTest, Mp3FormatGaplessOffload) {
doc::test("Check that compressed offload mix ports for MP3 implement gapless offload");
const auto& flags = getOutputFlags();
const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33;
// See test instantiation, only offload MP3 mix ports are used.
if (std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
return flag == toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD);
}) == flags.end()) {
if (isNewDeviceLaunchingOnTPlus) {
FAIL() << "New devices launching on Android T+ must support gapless offload, "
<< "see VSR-4.3-001";
} else {
GTEST_SKIP() << "Compressed offload mix port does not support gapless offload";
}
}
std::vector<uint8_t> offloadData;
ASSERT_NO_FATAL_FAILURE(loadData("/data/local/tmp/sine882hz3s.mp3", &offloadData));
ASSERT_FALSE(offloadData.empty());
ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
const int presentationeEndPrecisionMs = 1000;
const int sampleRate = 44100;
const int significantSampleNumber = (presentationeEndPrecisionMs * sampleRate) / 1000;
const int delay = 576 + 1000;
const int padding = 756 + 1000;
const int durationMs = 3000 - 44;
auto start = std::chrono::steady_clock::now();
auto callbacks = sp<OffloadCallbacks>::make();
std::mutex presentationEndLock;
std::vector<float> presentationEndTimes;
// StreamWriter plays 'offloadData' in a loop, possibly using multiple calls to 'write', this
// depends on the relative sizes of 'offloadData' and the HAL buffer. Writer calls 'onDataStart'
// each time it starts writing the buffer from the beginning, and 'onDataWrap' callback each
// time it wraps around the buffer.
StreamWriter writer(
stream.get(), stream->getBufferSize(), std::move(offloadData),
[&]() /* onDataStart */ { start = std::chrono::steady_clock::now(); },
[&]() /* onDataWrap */ {
Return<Result> ret(Result::OK);
// Decrease the volume since the test plays a loud sine wave.
ret = stream->setVolume(0.1, 0.1);
if (!ret.isOk() || ret != Result::OK) {
ALOGE("%s: setVolume failed: %s", __func__, toString(ret).c_str());
return false;
}
ret = Parameters::set(
stream, {{AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, std::to_string(delay)},
{AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, std::to_string(padding)}});
if (!ret.isOk() || ret != Result::OK) {
ALOGE("%s: setParameters failed: %s", __func__, toString(ret).c_str());
return false;
}
ret = stream->drain(AudioDrain::EARLY_NOTIFY);
if (!ret.isOk() || ret != Result::OK) {
ALOGE("%s: drain failed: %s", __func__, toString(ret).c_str());
return false;
}
// FIXME: On some SoCs intermittent errors are possible, ignore them.
if (callbacks->waitForDrainReadyOrError()) {
const float duration = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start)
.count();
std::lock_guard lg(presentationEndLock);
presentationEndTimes.push_back(duration);
}
return true;
});
ASSERT_OK(stream->setCallback(callbacks));
ASSERT_TRUE(writer.start());
// How many times to loop the track so that the sum of gapless delay and padding from
// the first presentation end to the last is at least 'presentationeEndPrecisionMs'.
const int playbackNumber = (int)(significantSampleNumber / ((float)delay + padding) + 1);
for (bool done = false; !done;) {
usleep(presentationeEndPrecisionMs * 1000);
{
std::lock_guard lg(presentationEndLock);
done = presentationEndTimes.size() >= playbackNumber;
}
ASSERT_FALSE(writer.hasError()) << "Recent write or drain operation has failed";
}
const float avgDuration =
std::accumulate(presentationEndTimes.begin(), presentationEndTimes.end(), 0.0) /
presentationEndTimes.size();
std::stringstream observedEndTimes;
std::copy(presentationEndTimes.begin(), presentationEndTimes.end(),
std::ostream_iterator<float>(observedEndTimes, ", "));
EXPECT_NEAR(durationMs, avgDuration, presentationeEndPrecisionMs * 0.1)
<< "Observed durations: " << observedEndTimes.str();
writer.stop();
EXPECT_OK(stream->clearCallback());
releasePatchIfNeeded();
}
INSTANTIATE_TEST_CASE_P(
CompressedOffloadOutputStream, CompressedOffloadOutputStreamTest,
::testing::ValuesIn(getOutputDeviceCompressedConfigParameters(AudioConfigBase{
.format = xsd::toString(xsd::AudioFormat::AUDIO_FORMAT_MP3),
.sampleRateHz = 44100,
.channelMask = xsd::toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO)})),
&DeviceConfigParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompressedOffloadOutputStreamTest);

View File

@@ -57,9 +57,6 @@ static std::vector<AudioConfig> combineAudioConfig(std::vector<xsd::AudioChannel
static std::tuple<std::vector<AudioInOutFlag>, bool> generateOutFlags(
const xsd::MixPorts::MixPort& mixPort) {
static const std::vector<AudioInOutFlag> offloadFlags = {
toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_DIRECT)};
std::vector<AudioInOutFlag> flags;
bool isOffload = false;
if (mixPort.hasFlags()) {
@@ -67,14 +64,10 @@ static std::tuple<std::vector<AudioInOutFlag>, bool> generateOutFlags(
isOffload = std::find(xsdFlags.begin(), xsdFlags.end(),
xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) !=
xsdFlags.end();
if (!isOffload) {
for (auto flag : xsdFlags) {
if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) {
flags.push_back(toString(flag));
}
for (auto flag : xsdFlags) {
if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) {
flags.push_back(toString(flag));
}
} else {
flags = offloadFlags;
}
}
return {flags, isOffload};
@@ -85,10 +78,10 @@ static AudioOffloadInfo generateOffloadInfo(const AudioConfigBase& base) {
.base = base,
.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
.bitRatePerSecond = 320,
.bitRatePerSecond = 192, // as in sine882hz3s.mp3
.durationMicroseconds = -1,
.bitWidth = 16,
.bufferSize = 256 // arbitrary value
.bufferSize = 72000 // 3 seconds at 192 kbps, as in sine882hz3s.mp3
};
}
@@ -100,11 +93,10 @@ std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(bool one
if (!module || !module->getFirstMixPorts()) break;
for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
if (mixPort.getRole() != xsd::Role::source) continue; // not an output profile
if (getCachedPolicyConfig()
.getAttachedSinkDeviceForMixPort(moduleName, mixPort.getName())
.empty()) {
continue; // no attached device
}
const auto attachedDeviceAddress =
getCachedPolicyConfig().getDeviceAddressOfSinkDeviceAttachedToMixPort(
moduleName, mixPort.getName());
if (!attachedDeviceAddress.has_value()) continue;
auto [flags, isOffload] = generateOutFlags(mixPort);
for (const auto& profile : mixPort.getProfile()) {
if (!profile.hasFormat() || !profile.hasSamplingRates() ||
@@ -118,7 +110,8 @@ std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(bool one
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(config.base));
}
result.emplace_back(device, mixPort.getName(), config, flags);
result.emplace_back(device, mixPort.getName(), attachedDeviceAddress.value(),
config, flags);
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
@@ -162,13 +155,16 @@ const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters
profile.getFormat(),
static_cast<uint32_t>(profile.getSamplingRates()[0]),
toString(profile.getChannelMasks()[0])};
DeviceAddress defaultDevice = {
toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT), {}};
{
AudioConfig config{.base = validBase};
config.base.channelMask = "random_string";
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(validBase));
}
result.emplace_back(device, mixPort.getName(), config, validFlags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
validFlags);
}
{
AudioConfig config{.base = validBase};
@@ -176,7 +172,8 @@ const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(validBase));
}
result.emplace_back(device, mixPort.getName(), config, validFlags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
validFlags);
}
if (generateInvalidFlags) {
AudioConfig config{.base = validBase};
@@ -184,32 +181,37 @@ const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters
config.offloadInfo.info(generateOffloadInfo(validBase));
}
std::vector<AudioInOutFlag> flags = {"random_string", ""};
result.emplace_back(device, mixPort.getName(), config, flags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
flags);
}
if (isOffload) {
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().base.channelMask = "random_string";
result.emplace_back(device, mixPort.getName(), config, validFlags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
validFlags);
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().base.format = "random_string";
result.emplace_back(device, mixPort.getName(), config, validFlags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
validFlags);
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().streamType = "random_string";
result.emplace_back(device, mixPort.getName(), config, validFlags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
validFlags);
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().usage = "random_string";
result.emplace_back(device, mixPort.getName(), config, validFlags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
validFlags);
}
hasOffloadConfig = true;
} else {
@@ -233,11 +235,10 @@ std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(bool oneP
if (!module || !module->getFirstMixPorts()) break;
for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
if (mixPort.getRole() != xsd::Role::sink) continue; // not an input profile
if (getCachedPolicyConfig()
.getAttachedSourceDeviceForMixPort(moduleName, mixPort.getName())
.empty()) {
continue; // no attached device
}
const auto attachedDeviceAddress =
getCachedPolicyConfig().getDeviceAddressOfSourceDeviceAttachedToMixPort(
moduleName, mixPort.getName());
if (!attachedDeviceAddress.has_value()) continue;
std::vector<AudioInOutFlag> flags;
if (mixPort.hasFlags()) {
std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(),
@@ -250,7 +251,8 @@ std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(bool oneP
auto configs = combineAudioConfig(profile.getChannelMasks(),
profile.getSamplingRates(), profile.getFormat());
for (const auto& config : configs) {
result.emplace_back(device, mixPort.getName(), config, flags);
result.emplace_back(device, mixPort.getName(), attachedDeviceAddress.value(),
config, flags);
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
@@ -298,20 +300,25 @@ const std::vector<DeviceConfigParameter>& getInputDeviceInvalidConfigParameters(
profile.getFormat(),
static_cast<uint32_t>(profile.getSamplingRates()[0]),
toString(profile.getChannelMasks()[0])};
DeviceAddress defaultDevice = {
toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT), {}};
{
AudioConfig config{.base = validBase};
config.base.channelMask = "random_string";
result.emplace_back(device, mixPort.getName(), config, validFlags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
validFlags);
}
{
AudioConfig config{.base = validBase};
config.base.format = "random_string";
result.emplace_back(device, mixPort.getName(), config, validFlags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
validFlags);
}
if (generateInvalidFlags) {
AudioConfig config{.base = validBase};
std::vector<AudioInOutFlag> flags = {"random_string", ""};
result.emplace_back(device, mixPort.getName(), config, flags);
result.emplace_back(device, mixPort.getName(), defaultDevice, config,
flags);
}
hasConfig = true;
break;

View File

@@ -61,6 +61,18 @@ class PolicyConfig {
const std::set<std::string>& getModulesWithDevicesNames() const {
return mModulesWithDevicesNames;
}
std::optional<DeviceAddress> getDeviceAddressOfSinkDeviceAttachedToMixPort(
const std::string& moduleName, const std::string& mixPortName) const {
const auto attachedDevicePort = getAttachedSinkDeviceForMixPort(moduleName, mixPortName);
if (attachedDevicePort.empty()) return {};
return getDeviceAddressOfDevicePort(moduleName, attachedDevicePort);
}
std::optional<DeviceAddress> getDeviceAddressOfSourceDeviceAttachedToMixPort(
const std::string& moduleName, const std::string& mixPortName) const {
const auto attachedDevicePort = getAttachedSourceDeviceForMixPort(moduleName, mixPortName);
if (attachedDevicePort.empty()) return {};
return getDeviceAddressOfDevicePort(moduleName, attachedDevicePort);
}
std::string getAttachedSinkDeviceForMixPort(const std::string& moduleName,
const std::string& mixPortName) const {
return findAttachedDevice(getAttachedDevices(moduleName),
@@ -84,8 +96,6 @@ class PolicyConfig {
const std::vector<std::string>& getAttachedDevices(const std::string& moduleName) const;
std::optional<DeviceAddress> getDeviceAddressOfDevicePort(
const std::string& moduleName, const std::string& devicePortName) const;
std::string getDevicePortTagNameFromType(const std::string& moduleName,
const AudioDevice& deviceType) const;
std::set<std::string> getSinkDevicesForMixPort(const std::string& moduleName,
const std::string& mixPortName) const;
std::set<std::string> getSourceDevicesForMixPort(const std::string& moduleName,

View File

@@ -34,6 +34,7 @@ cc_defaults {
],
shared_libs: [
"libbinder",
"libcutils",
"libfmq",
"libxml2",
],
@@ -190,6 +191,7 @@ cc_test {
],
data: [
":audio_policy_configuration_V7_0",
"data/sine882hz3s.mp3",
],
// Use test_config for vts suite.
// TODO(b/146104851): Add auto-gen rules and remove it.
@@ -223,6 +225,7 @@ cc_test {
],
data: [
":audio_policy_configuration_V7_1",
"data/sine882hz3s.mp3",
],
// Use test_config for vts suite.
// TODO(b/146104851): Add auto-gen rules and remove it.

View File

@@ -617,7 +617,8 @@ static std::string DeviceConfigParameterToString(
std::get<PARAM_FLAGS>(info.param)));
#elif MAJOR_VERSION >= 7
const auto configPart =
std::to_string(config.base.sampleRateHz) + "_" +
::testing::PrintToString(std::get<PARAM_ATTACHED_DEV_ADDR>(info.param).deviceType) +
"_" + std::to_string(config.base.sampleRateHz) + "_" +
// The channel masks and flags are vectors of strings, just need to sanitize them.
SanitizeStringForGTestName(::testing::PrintToString(config.base.channelMask)) + "_" +
SanitizeStringForGTestName(::testing::PrintToString(std::get<PARAM_FLAGS>(info.param)));
@@ -658,6 +659,9 @@ class AudioHidlTestWithDeviceConfigParameter
std::get<INDEX_OUTPUT>(std::get<PARAM_FLAGS>(GetParam())));
}
#elif MAJOR_VERSION >= 7
DeviceAddress getAttachedDeviceAddress() const {
return std::get<PARAM_ATTACHED_DEV_ADDR>(GetParam());
}
hidl_vec<AudioInOutFlag> getInputFlags() const { return std::get<PARAM_FLAGS>(GetParam()); }
hidl_vec<AudioInOutFlag> getOutputFlags() const { return std::get<PARAM_FLAGS>(GetParam()); }
#endif
@@ -933,6 +937,15 @@ class StreamWriter : public StreamWorker<StreamWriter> {
StreamWriter(IStreamOut* stream, size_t bufferSize)
: mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {}
StreamWriter(IStreamOut* stream, size_t bufferSize, std::vector<uint8_t>&& data,
std::function<void()> onDataStart, std::function<bool()> onDataWrap)
: mStream(stream),
mBufferSize(bufferSize),
mData(std::move(data)),
mOnDataStart(onDataStart),
mOnDataWrap(onDataWrap) {
ALOGI("StreamWriter data size: %d", (int)mData.size());
}
~StreamWriter() {
stop();
if (mEfGroup) {
@@ -998,9 +1011,12 @@ class StreamWriter : public StreamWorker<StreamWriter> {
ALOGE("command message queue write failed");
return false;
}
const size_t dataSize = std::min(mData.size(), mDataMQ->availableToWrite());
bool success = mDataMQ->write(mData.data(), dataSize);
if (mDataPosition == 0) mOnDataStart();
const size_t dataSize = std::min(mData.size() - mDataPosition, mDataMQ->availableToWrite());
bool success = mDataMQ->write(mData.data() + mDataPosition, dataSize);
ALOGE_IF(!success, "data message queue write failed");
mDataPosition += dataSize;
if (mDataPosition >= mData.size()) mDataPosition = 0;
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
uint32_t efState = 0;
@@ -1026,6 +1042,9 @@ class StreamWriter : public StreamWorker<StreamWriter> {
ALOGE("bad wait status: %d", ret);
success = false;
}
if (success && mDataPosition == 0) {
success = mOnDataWrap();
}
return success;
}
@@ -1033,6 +1052,9 @@ class StreamWriter : public StreamWorker<StreamWriter> {
IStreamOut* const mStream;
const size_t mBufferSize;
std::vector<uint8_t> mData;
std::function<void()> mOnDataStart = []() {};
std::function<bool()> mOnDataWrap = []() { return true; };
size_t mDataPosition = 0;
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<DataMQ> mDataMQ;
std::unique_ptr<StatusMQ> mStatusMQ;
@@ -1047,7 +1069,7 @@ class OutputStreamTest
#if MAJOR_VERSION <= 6
address.device = AudioDevice::OUT_DEFAULT;
#elif MAJOR_VERSION >= 7
address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT);
address = getAttachedDeviceAddress();
#endif
const AudioConfig& config = getConfig();
auto flags = getOutputFlags();
@@ -1243,16 +1265,11 @@ class InputStreamTest
#if MAJOR_VERSION <= 6
address.device = AudioDevice::IN_DEFAULT;
#elif MAJOR_VERSION >= 7
auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
getDeviceName(), getMixPortName());
address = getAttachedDeviceAddress();
auto& metadata = initMetadata.tracks[0];
if (maybeSourceAddress.has_value() &&
!xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType)) {
address = maybeSourceAddress.value();
if (!xsd::isTelephonyDevice(address.deviceType)) {
metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_UNPROCESSED);
metadata.channelMask = getConfig().base.channelMask;
} else {
address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
}
#if MAJOR_VERSION == 7 && MINOR_VERSION >= 1
auto flagsIt = std::find(flags.begin(), flags.end(),

View File

@@ -39,9 +39,10 @@ using DeviceConfigParameter = std::tuple<
std::variant<android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::AudioInputFlag,
android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::AudioOutputFlag>>;
#elif MAJOR_VERSION >= 7
enum { PARAM_DEVICE, PARAM_PORT_NAME, PARAM_CONFIG, PARAM_FLAGS };
enum { PARAM_DEVICE, PARAM_PORT_NAME, PARAM_ATTACHED_DEV_ADDR, PARAM_CONFIG, PARAM_FLAGS };
using DeviceConfigParameter =
std::tuple<DeviceParameter, std::string,
android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::DeviceAddress,
android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::AudioConfig,
std::vector<android::hardware::audio::CORE_TYPES_CPP_VERSION::AudioInOutFlag>>;
#endif

View File

@@ -29,6 +29,7 @@
<option name="cleanup" value="true" />
<option name="push" value="VtsHalAudioV7_0TargetTest->/data/local/tmp/VtsHalAudioV7_0TargetTest" />
<option name="push" value="audio_policy_configuration_V7_0.xsd->/data/local/tmp/audio_policy_configuration_V7_0.xsd" />
<option name="push" value="sine882hz3s.mp3->/data/local/tmp/sine882hz3s.mp3" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >

View File

@@ -29,6 +29,8 @@
<option name="cleanup" value="true" />
<option name="push" value="VtsHalAudioV7_1TargetTest->/data/local/tmp/VtsHalAudioV7_1TargetTest" />
<option name="push" value="audio_policy_configuration_V7_1.xsd->/data/local/tmp/audio_policy_configuration_V7_1.xsd" />
<option name="push" value="sine882hz3s.mp3->/data/local/tmp/sine882hz3s.mp3" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >

View File

@@ -18,6 +18,7 @@
#include "HandleImporter.h"
#include <gralloctypes/Gralloc4.h>
#include "aidl/android/hardware/graphics/common/Smpte2086.h"
#include <log/log.h>
namespace android {
@@ -30,6 +31,7 @@ namespace helper {
using aidl::android::hardware::graphics::common::PlaneLayout;
using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
using aidl::android::hardware::graphics::common::Smpte2086;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
using MapperErrorV2 = android::hardware::graphics::mapper::V2_0::Error;
using MapperErrorV3 = android::hardware::graphics::mapper::V3_0::Error;
@@ -127,16 +129,35 @@ YCbCrLayout HandleImporter::lockYCbCrInternal(const sp<M> mapper, buffer_handle_
bool isMetadataPesent(const sp<IMapperV4> mapper, const buffer_handle_t& buf,
MetadataType metadataType) {
auto buffer = const_cast<native_handle_t*>(buf);
mapper->get(buffer, metadataType, [] (const auto& tmpError,
bool ret = false;
hidl_vec<uint8_t> vec;
mapper->get(buffer, metadataType, [&] (const auto& tmpError,
const auto& tmpMetadata) {
if (tmpError == MapperErrorV4::NONE) {
return tmpMetadata.size() > 0;
vec = tmpMetadata;
} else {
ALOGE("%s: failed to get metadata %d!", __FUNCTION__, tmpError);
return false;
}});
return false;
if (vec.size() > 0) {
if (metadataType == gralloc4::MetadataType_Smpte2086){
std::optional<Smpte2086> realSmpte2086;
gralloc4::decodeSmpte2086(vec, &realSmpte2086);
ret = realSmpte2086.has_value();
} else if (metadataType == gralloc4::MetadataType_Smpte2094_10) {
std::optional<std::vector<uint8_t>> realSmpte2094_10;
gralloc4::decodeSmpte2094_10(vec, &realSmpte2094_10);
ret = realSmpte2094_10.has_value();
} else if (metadataType == gralloc4::MetadataType_Smpte2094_40) {
std::optional<std::vector<uint8_t>> realSmpte2094_40;
gralloc4::decodeSmpte2094_40(vec, &realSmpte2094_40);
ret = realSmpte2094_40.has_value();
} else {
ALOGE("%s: Unknown metadata type!", __FUNCTION__);
}
}
return ret;
}
std::vector<PlaneLayout> getPlaneLayouts(const sp<IMapperV4> mapper, buffer_handle_t& buf) {

View File

@@ -1739,6 +1739,10 @@ TEST_P(CameraAidlTest, processUltraHighResolutionRequest) {
std::list<PixelFormat> pixelFormats = {PixelFormat::YCBCR_420_888, PixelFormat::RAW16};
for (PixelFormat format : pixelFormats) {
previewStream.usage =
static_cast<aidl::android::hardware::graphics::common::BufferUsage>(
GRALLOC1_CONSUMER_USAGE_CPU_READ);
previewStream.dataSpace = Dataspace::UNKNOWN;
configureStreams(name, mProvider, format, &mSession, &previewStream, &halStreams,
&supportsPartialResults, &partialResultCount, &useHalBufManager, &cb,
0, /*maxResolution*/ true);
@@ -1843,7 +1847,6 @@ TEST_P(CameraAidlTest, processUltraHighResolutionRequest) {
TEST_P(CameraAidlTest, process10BitDynamicRangeRequest) {
std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
int64_t bufferId = 1;
int32_t frameNumber = 1;
CameraMetadata settings;
for (const auto& name : cameraDeviceNames) {
@@ -1866,7 +1869,7 @@ TEST_P(CameraAidlTest, process10BitDynamicRangeRequest) {
CameraMetadata req;
android::hardware::camera::common::V1_0::helper::CameraMetadata defaultSettings;
ndk::ScopedAStatus ret =
mSession->constructDefaultRequestSettings(RequestTemplate::STILL_CAPTURE, &req);
mSession->constructDefaultRequestSettings(RequestTemplate::PREVIEW, &req);
ASSERT_TRUE(ret.isOk());
const camera_metadata_t* metadata =
@@ -1896,6 +1899,10 @@ TEST_P(CameraAidlTest, process10BitDynamicRangeRequest) {
Stream previewStream;
std::shared_ptr<DeviceCb> cb;
for (const auto& profile : profileList) {
previewStream.usage =
static_cast<aidl::android::hardware::graphics::common::BufferUsage>(
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER);
previewStream.dataSpace = getDataspace(PixelFormat::IMPLEMENTATION_DEFINED);
configureStreams(name, mProvider, PixelFormat::IMPLEMENTATION_DEFINED, &mSession,
&previewStream, &halStreams, &supportsPartialResults,
&partialResultCount, &useHalBufManager, &cb, 0,
@@ -1916,63 +1923,75 @@ TEST_P(CameraAidlTest, process10BitDynamicRangeRequest) {
// Don't use the queue onwards.
}
std::vector<buffer_handle_t> graphicBuffers;
graphicBuffers.reserve(halStreams.size());
mInflightMap.clear();
// Stream as long as needed to fill the Hal inflight queue
std::vector<CaptureRequest> requests(halStreams[0].maxBuffers);
std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>(
static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults,
partialResultCount, std::unordered_set<std::string>(), resultQueue);
for (int32_t frameNumber = 0; frameNumber < requests.size(); frameNumber++) {
std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>(
static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults,
partialResultCount, std::unordered_set<std::string>(), resultQueue);
std::vector<CaptureRequest> requests(1);
CaptureRequest& request = requests[0];
std::vector<StreamBuffer>& outputBuffers = request.outputBuffers;
outputBuffers.resize(halStreams.size());
CaptureRequest& request = requests[frameNumber];
std::vector<StreamBuffer>& outputBuffers = request.outputBuffers;
outputBuffers.resize(halStreams.size());
size_t k = 0;
for (const auto& halStream : halStreams) {
buffer_handle_t buffer_handle;
if (useHalBufManager) {
outputBuffers[k] = {halStream.id, 0,
NativeHandle(), BufferStatus::OK,
NativeHandle(), NativeHandle()};
} else {
allocateGraphicBuffer(previewStream.width, previewStream.height,
android_convertGralloc1To0Usage(
static_cast<uint64_t>(halStream.producerUsage),
static_cast<uint64_t>(halStream.consumerUsage)),
halStream.overrideFormat, &buffer_handle);
size_t k = 0;
inflightReq->mOutstandingBufferIds.resize(halStreams.size());
std::vector<buffer_handle_t> graphicBuffers;
graphicBuffers.reserve(halStreams.size());
graphicBuffers.push_back(buffer_handle);
outputBuffers[k] = {
halStream.id, bufferId, android::makeToAidl(buffer_handle),
BufferStatus::OK, NativeHandle(), NativeHandle()};
bufferId++;
for (const auto& halStream : halStreams) {
buffer_handle_t buffer_handle;
if (useHalBufManager) {
outputBuffers[k] = {halStream.id, 0,
NativeHandle(), BufferStatus::OK,
NativeHandle(), NativeHandle()};
} else {
auto usage = android_convertGralloc1To0Usage(
static_cast<uint64_t>(halStream.producerUsage),
static_cast<uint64_t>(halStream.consumerUsage));
allocateGraphicBuffer(previewStream.width, previewStream.height, usage,
halStream.overrideFormat, &buffer_handle);
inflightReq->mOutstandingBufferIds[halStream.id][bufferId] = buffer_handle;
graphicBuffers.push_back(buffer_handle);
outputBuffers[k] = {halStream.id, bufferId,
android::makeToAidl(buffer_handle), BufferStatus::OK, NativeHandle(),
NativeHandle()};
bufferId++;
}
k++;
}
k++;
}
request.inputBuffer = {
-1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()};
request.frameNumber = frameNumber;
request.fmqSettingsSize = 0;
request.settings = settings;
request.inputWidth = 0;
request.inputHeight = 0;
request.inputBuffer = {
-1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()};
request.frameNumber = frameNumber;
request.fmqSettingsSize = 0;
request.settings = settings;
request.inputWidth = 0;
request.inputHeight = 0;
{
std::unique_lock<std::mutex> l(mLock);
mInflightMap[frameNumber] = inflightReq;
}
{
std::unique_lock<std::mutex> l(mLock);
mInflightMap.clear();
mInflightMap[frameNumber] = inflightReq;
}
int32_t numRequestProcessed = 0;
std::vector<BufferCache> cachesToRemove;
ndk::ScopedAStatus returnStatus =
mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed);
mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed);
ASSERT_TRUE(returnStatus.isOk());
ASSERT_EQ(numRequestProcessed, 1u);
ASSERT_EQ(numRequestProcessed, requests.size());
{
returnStatus = mSession->repeatingRequestEnd(requests.size() - 1,
std::vector<int32_t> {halStreams[0].id});
ASSERT_TRUE(returnStatus.isOk());
for (int32_t frameNumber = 0; frameNumber < requests.size(); frameNumber++) {
const auto& inflightReq = mInflightMap[frameNumber];
std::unique_lock<std::mutex> l(mLock);
while (!inflightReq->errorCodeValid &&
((0 < inflightReq->numBuffersLeft) || (!inflightReq->haveResultMetadata))) {
@@ -1985,6 +2004,7 @@ TEST_P(CameraAidlTest, process10BitDynamicRangeRequest) {
ASSERT_NE(inflightReq->resultOutputBuffers.size(), 0u);
verify10BitMetadata(mHandleImporter, *inflightReq, profile);
}
if (useHalBufManager) {
std::vector<int32_t> streamIds(halStreams.size());
for (size_t i = 0; i < streamIds.size(); i++) {

View File

@@ -2639,8 +2639,20 @@ void CameraAidlTest::configureStreams(const std::string& name,
outputStreams.clear();
Size maxSize;
auto rc = getMaxOutputSizeForFormat(staticMeta, format, &maxSize, maxResolution);
ASSERT_EQ(Status::OK, rc);
if (maxResolution) {
auto rc = getMaxOutputSizeForFormat(staticMeta, format, &maxSize, maxResolution);
ASSERT_EQ(Status::OK, rc);
} else {
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
static_cast<int32_t>(format)};
auto rc = getAvailableOutputStreams(staticMeta, outputStreams, &previewThreshold);
ASSERT_EQ(Status::OK, rc);
ASSERT_FALSE(outputStreams.empty());
maxSize.width = outputStreams[0].width;
maxSize.height = outputStreams[0].height;
}
std::vector<Stream> streams(1);
streams[0] = {0,
@@ -2648,9 +2660,8 @@ void CameraAidlTest::configureStreams(const std::string& name,
maxSize.width,
maxSize.height,
format,
static_cast<::aidl::android::hardware::graphics::common::BufferUsage>(
GRALLOC1_CONSUMER_USAGE_CPU_READ),
Dataspace::UNKNOWN,
previewStream->usage,
previewStream->dataSpace,
StreamRotation::ROTATION_0,
"",
0,
@@ -2736,7 +2747,8 @@ void CameraAidlTest::verify10BitMetadata(
HandleImporter& importer, const InFlightRequest& request,
aidl::android::hardware::camera::metadata::RequestAvailableDynamicRangeProfilesMap
profile) {
for (const auto& b : request.resultOutputBuffers) {
for (auto b : request.resultOutputBuffers) {
importer.importBuffer(b.buffer.buffer);
bool smpte2086Present = importer.isSmpte2086Present(b.buffer.buffer);
bool smpte2094_10Present = importer.isSmpte2094_10Present(b.buffer.buffer);
bool smpte2094_40Present = importer.isSmpte2094_40Present(b.buffer.buffer);
@@ -2753,7 +2765,6 @@ void CameraAidlTest::verify10BitMetadata(
ASSERT_FALSE(smpte2094_40Present);
break;
case ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS:
ASSERT_FALSE(smpte2086Present);
ASSERT_FALSE(smpte2094_10Present);
ASSERT_TRUE(smpte2094_40Present);
break;
@@ -2774,6 +2785,7 @@ void CameraAidlTest::verify10BitMetadata(
profile);
ADD_FAILURE();
}
importer.freeBuffer(b.buffer.buffer);
}
}

View File

@@ -399,6 +399,10 @@ class CameraAidlTest : public ::testing::TestWithParam<std::string> {
// Result metadata
::android::hardware::camera::common::V1_0::helper::CameraMetadata collectedResult;
// Inflight buffers
using OutstandingBuffers = std::unordered_map<uint64_t, buffer_handle_t>;
std::vector<OutstandingBuffers> mOutstandingBufferIds;
// A copy-able StreamBuffer using buffer_handle_t instead of AIDLs NativeHandle
struct NativeStreamBuffer {
int32_t streamId;

View File

@@ -155,7 +155,7 @@ ScopedAStatus DeviceCb::requestStreamBuffers(const std::vector<BufferRequest>& b
BufferStatus::OK, NativeHandle(), NativeHandle(),
};
mOutstandingBufferIds[idx][mNextBufferId++] = ::android::dupToAidl(handle);
mOutstandingBufferIds[idx][mNextBufferId++] = handle;
}
atLeastOneStreamOk = true;
bufRets[i].streamId = stream.id;
@@ -427,9 +427,13 @@ bool DeviceCb::processCaptureResultLocked(
}
CameraAidlTest::InFlightRequest::StreamBufferAndTimestamp streamBufferAndTimestamp;
auto outstandingBuffers = mUseHalBufManager ? mOutstandingBufferIds :
request->mOutstandingBufferIds;
auto outputBuffer = outstandingBuffers.empty() ? ::android::makeFromAidl(buffer.buffer) :
outstandingBuffers[buffer.streamId][buffer.bufferId];
streamBufferAndTimestamp.buffer = {buffer.streamId,
buffer.bufferId,
::android::makeFromAidl(buffer.buffer),
outputBuffer,
buffer.status,
::android::makeFromAidl(buffer.acquireFence),
::android::makeFromAidl(buffer.releaseFence)};

View File

@@ -73,7 +73,7 @@ class DeviceCb : public BnCameraDeviceCallback {
std::vector<Stream> mStreams;
std::vector<HalStream> mHalStreams;
int64_t mNextBufferId = 1;
using OutstandingBuffers = std::unordered_map<uint64_t, NativeHandle>;
using OutstandingBuffers = std::unordered_map<uint64_t, buffer_handle_t>;
// size == mStreams.size(). Tracking each streams outstanding buffers
std::vector<OutstandingBuffers> mOutstandingBufferIds;
std::condition_variable mFlushedCondition;

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<configuration description="Runs VtsAidlHalSensorsTargetTest.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.StopServicesSetup"/>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="VtsAidlHalSensorsTargetTest->/data/local/tmp/VtsAidlHalSensorsTargetTest" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-timeout" value="900000" />
<option name="runtime-hint" value="300000"/>
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsAidlHalSensorsTargetTest" />
</test>
</configuration>