1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <fstream>
18 #include <numeric>
19
20 #include <android-base/chrono_utils.h>
21 #include <cutils/properties.h>
22
23 #include "Generators.h"
24
25 // pull in all the <= 6.0 tests
26 #include "6.0/AudioPrimaryHidlHalTest.cpp"
27
28 class InvalidInputConfigNoFlagsTest : public AudioHidlTestWithDeviceConfigParameter {};
TEST_P(InvalidInputConfigNoFlagsTest,InputBufferSizeTest)29 TEST_P(InvalidInputConfigNoFlagsTest, InputBufferSizeTest) {
30 doc::test("Verify that invalid config is rejected by IDevice::getInputBufferSize method.");
31 uint64_t bufferSize;
32 ASSERT_OK(getDevice()->getInputBufferSize(getConfig(), returnIn(res, bufferSize)));
33 EXPECT_EQ(Result::INVALID_ARGUMENTS, res);
34 }
35 INSTANTIATE_TEST_CASE_P(
36 InputBufferSizeInvalidConfig, InvalidInputConfigNoFlagsTest,
37 ::testing::ValuesIn(getInputDeviceInvalidConfigParameters(false /*generateInvalidFlags*/)),
38 &DeviceConfigParameterToString);
39 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InvalidInputConfigNoFlagsTest);
40
getValidInputDeviceAddress()41 static const DeviceAddress& getValidInputDeviceAddress() {
42 static const DeviceAddress valid = {
43 .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT)};
44 return valid;
45 }
46
getValidOutputDeviceAddress()47 static const DeviceAddress& getValidOutputDeviceAddress() {
48 static const DeviceAddress valid = {
49 .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT)};
50 return valid;
51 }
52
getInvalidDeviceAddress()53 static const DeviceAddress& getInvalidDeviceAddress() {
54 static const DeviceAddress valid = {.deviceType = "random_string"};
55 return valid;
56 }
57
TEST_P(AudioHidlDeviceTest,SetConnectedStateInvalidDeviceAddress)58 TEST_P(AudioHidlDeviceTest, SetConnectedStateInvalidDeviceAddress) {
59 doc::test("Check that invalid device address is rejected by IDevice::setConnectedState");
60 EXPECT_RESULT(invalidArgsOrNotSupported,
61 getDevice()->setConnectedState(getInvalidDeviceAddress(), true));
62 EXPECT_RESULT(invalidArgsOrNotSupported,
63 getDevice()->setConnectedState(getInvalidDeviceAddress(), false));
64 }
65
generatePortConfigs(bool valid)66 static std::vector<AudioPortConfig>& generatePortConfigs(bool valid) {
67 enum { // Note: This is for convenience when deriving "invalid" configs from "valid".
68 PORT_CONF_MINIMAL,
69 PORT_CONF_WITH_GAIN,
70 PORT_CONF_EXT_DEVICE,
71 PORT_CONF_EXT_MIX_SOURCE,
72 PORT_CONF_EXT_MIX_SINK,
73 PORT_CONF_EXT_SESSION
74 };
75 static std::vector<AudioPortConfig> valids = [] {
76 std::vector<AudioPortConfig> result;
77 result.reserve(PORT_CONF_EXT_SESSION + 1);
78 result.push_back(AudioPortConfig{});
79 AudioPortConfig configWithGain{};
80 configWithGain.gain.config(AudioGainConfig{
81 .index = 0,
82 .mode = {toString(xsd::AudioGainMode::AUDIO_GAIN_MODE_JOINT)},
83 .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
84 .rampDurationMs = 1});
85 configWithGain.gain.config().values.resize(1);
86 configWithGain.gain.config().values[0] = 1000;
87 result.push_back(std::move(configWithGain));
88 AudioPortConfig configWithPortExtDevice{};
89 configWithPortExtDevice.ext.device(getValidOutputDeviceAddress());
90 result.push_back(std::move(configWithPortExtDevice));
91 AudioPortConfig configWithPortExtMixSource{};
92 configWithPortExtMixSource.ext.mix({});
93 configWithPortExtMixSource.ext.mix().useCase.stream(
94 toString(xsd::AudioStreamType::AUDIO_STREAM_VOICE_CALL));
95 result.push_back(std::move(configWithPortExtMixSource));
96 AudioPortConfig configWithPortExtMixSink{};
97 configWithPortExtMixSink.ext.mix({});
98 configWithPortExtMixSink.ext.mix().useCase.source(
99 toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT));
100 result.push_back(std::move(configWithPortExtMixSink));
101 AudioPortConfig configWithPortExtSession{};
102 configWithPortExtSession.ext.session(
103 static_cast<AudioSession>(AudioSessionConsts::OUTPUT_MIX));
104 result.push_back(std::move(configWithPortExtSession));
105 return result;
106 }();
107 static std::vector<AudioPortConfig> invalids = [&] {
108 std::vector<AudioPortConfig> result;
109 AudioPortConfig invalidBaseChannelMask = valids[PORT_CONF_MINIMAL];
110 invalidBaseChannelMask.base.channelMask.value("random_string");
111 result.push_back(std::move(invalidBaseChannelMask));
112 AudioPortConfig invalidBaseFormat = valids[PORT_CONF_MINIMAL];
113 invalidBaseFormat.base.format.value("random_string");
114 result.push_back(std::move(invalidBaseFormat));
115 AudioPortConfig invalidGainMode = valids[PORT_CONF_WITH_GAIN];
116 invalidGainMode.gain.config().mode = {{"random_string"}};
117 result.push_back(std::move(invalidGainMode));
118 AudioPortConfig invalidGainChannelMask = valids[PORT_CONF_WITH_GAIN];
119 invalidGainChannelMask.gain.config().channelMask = "random_string";
120 result.push_back(std::move(invalidGainChannelMask));
121 AudioPortConfig invalidDeviceType = valids[PORT_CONF_EXT_DEVICE];
122 invalidDeviceType.ext.device().deviceType = "random_string";
123 result.push_back(std::move(invalidDeviceType));
124 AudioPortConfig invalidStreamType = valids[PORT_CONF_EXT_MIX_SOURCE];
125 invalidStreamType.ext.mix().useCase.stream() = "random_string";
126 result.push_back(std::move(invalidStreamType));
127 AudioPortConfig invalidSource = valids[PORT_CONF_EXT_MIX_SINK];
128 invalidSource.ext.mix().useCase.source() = "random_string";
129 result.push_back(std::move(invalidSource));
130 return result;
131 }();
132 return valid ? valids : invalids;
133 }
134
TEST_P(AudioHidlDeviceTest,SetAudioPortConfigInvalidArguments)135 TEST_P(AudioHidlDeviceTest, SetAudioPortConfigInvalidArguments) {
136 doc::test("Check that invalid port configs are rejected by IDevice::setAudioPortConfig");
137 for (const auto& invalidConfig : generatePortConfigs(false /*valid*/)) {
138 EXPECT_RESULT(invalidArgsOrNotSupported, getDevice()->setAudioPortConfig(invalidConfig))
139 << ::testing::PrintToString(invalidConfig);
140 }
141 }
142
TEST_P(AudioPatchHidlTest,CreatePatchInvalidArguments)143 TEST_P(AudioPatchHidlTest, CreatePatchInvalidArguments) {
144 doc::test("Check that invalid port configs are rejected by IDevice::createAudioPatch");
145 // Note that HAL actually might reject the proposed source / sink combo
146 // due to other reasons than presence of invalid enum-strings.
147 // TODO: Come up with a way to guarantee validity of a source / sink combo.
148 for (const auto& validSource : generatePortConfigs(true /*valid*/)) {
149 for (const auto& invalidSink : generatePortConfigs(false /*valid*/)) {
150 AudioPatchHandle handle;
151 EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{validSource},
152 hidl_vec<AudioPortConfig>{invalidSink},
153 returnIn(res, handle)));
154 EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
155 << "Source: " << ::testing::PrintToString(validSource)
156 << "; Sink: " << ::testing::PrintToString(invalidSink);
157 }
158 }
159 for (const auto& validSink : generatePortConfigs(true /*valid*/)) {
160 for (const auto& invalidSource : generatePortConfigs(false /*valid*/)) {
161 AudioPatchHandle handle;
162 EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{invalidSource},
163 hidl_vec<AudioPortConfig>{validSink},
164 returnIn(res, handle)));
165 EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
166 << "Source: " << ::testing::PrintToString(invalidSource)
167 << "; Sink: " << ::testing::PrintToString(validSink);
168 }
169 }
170 }
171
TEST_P(AudioPatchHidlTest,UpdatePatchInvalidArguments)172 TEST_P(AudioPatchHidlTest, UpdatePatchInvalidArguments) {
173 doc::test("Check that invalid port configs are rejected by IDevice::updateAudioPatch");
174 // Note that HAL actually might reject the proposed source / sink combo
175 // due to other reasons than presence of invalid enum-strings.
176 // TODO: Come up with a way to guarantee validity of a source / sink combo.
177 for (const auto& validSource : generatePortConfigs(true /*valid*/)) {
178 for (const auto& invalidSink : generatePortConfigs(false /*valid*/)) {
179 AudioPatchHandle handle{};
180 EXPECT_OK(getDevice()->updateAudioPatch(handle, hidl_vec<AudioPortConfig>{validSource},
181 hidl_vec<AudioPortConfig>{invalidSink},
182 returnIn(res, handle)));
183 EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
184 << "Source: " << ::testing::PrintToString(validSource)
185 << "; Sink: " << ::testing::PrintToString(invalidSink);
186 }
187 }
188 for (const auto& validSink : generatePortConfigs(true /*valid*/)) {
189 for (const auto& invalidSource : generatePortConfigs(false /*valid*/)) {
190 AudioPatchHandle handle{};
191 EXPECT_OK(getDevice()->updateAudioPatch(
192 handle, hidl_vec<AudioPortConfig>{invalidSource},
193 hidl_vec<AudioPortConfig>{validSink}, returnIn(res, handle)));
194 EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
195 << "Source: " << ::testing::PrintToString(invalidSource)
196 << "; Sink: " << ::testing::PrintToString(validSink);
197 }
198 }
199 }
200
201 enum { PARAM_DEVICE_CONFIG, PARAM_ADDRESS, PARAM_METADATA };
202 enum { INDEX_SINK, INDEX_SOURCE };
203 using SinkOrSourceMetadata = std::variant<SinkMetadata, SourceMetadata>;
204 using StreamOpenParameter = std::tuple<DeviceConfigParameter, DeviceAddress, SinkOrSourceMetadata>;
205
StreamOpenParameterToString(const::testing::TestParamInfo<StreamOpenParameter> & info)206 static std::string StreamOpenParameterToString(
207 const ::testing::TestParamInfo<StreamOpenParameter>& info) {
208 return DeviceConfigParameterToString(::testing::TestParamInfo<DeviceConfigParameter>{
209 std::get<PARAM_DEVICE_CONFIG>(info.param), info.index}) +
210 "__" +
211 SanitizeStringForGTestName(
212 ::testing::PrintToString(std::get<PARAM_ADDRESS>(info.param))) +
213 "__" +
214 SanitizeStringForGTestName(std::visit(
215 [](auto&& arg) -> std::string { return ::testing::PrintToString(arg); },
216 std::get<PARAM_METADATA>(info.param)));
217 }
218
219 class StreamOpenTest : public HidlTest, public ::testing::WithParamInterface<StreamOpenParameter> {
220 protected:
SetUp()221 void SetUp() override {
222 ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp()); // setup base
223 ASSERT_TRUE(getDevicesFactory() != nullptr);
224 ASSERT_TRUE(getDevice() != nullptr);
225 }
getFactoryName() const226 const std::string& getFactoryName() const override {
227 return std::get<PARAM_FACTORY_NAME>(
228 std::get<PARAM_DEVICE>(std::get<PARAM_DEVICE_CONFIG>(GetParam())));
229 }
getDeviceName() const230 const std::string& getDeviceName() const override {
231 return std::get<PARAM_DEVICE_NAME>(
232 std::get<PARAM_DEVICE>(std::get<PARAM_DEVICE_CONFIG>(GetParam())));
233 }
getConfig() const234 const AudioConfig& getConfig() const {
235 return std::get<PARAM_CONFIG>(std::get<PARAM_DEVICE_CONFIG>(GetParam()));
236 }
getFlags() const237 const hidl_vec<AudioInOutFlag> getFlags() const {
238 return std::get<PARAM_FLAGS>(std::get<PARAM_DEVICE_CONFIG>(GetParam()));
239 }
getDeviceAddress() const240 const DeviceAddress& getDeviceAddress() const { return std::get<PARAM_ADDRESS>(GetParam()); }
isParamForInputStream() const241 bool isParamForInputStream() const {
242 return std::get<PARAM_METADATA>(GetParam()).index() == INDEX_SINK;
243 }
getSinkMetadata() const244 const SinkMetadata& getSinkMetadata() const {
245 return std::get<INDEX_SINK>(std::get<PARAM_METADATA>(GetParam()));
246 }
getSourceMetadata() const247 const SourceMetadata& getSourceMetadata() const {
248 return std::get<INDEX_SOURCE>(std::get<PARAM_METADATA>(GetParam()));
249 }
250 };
251
getValidRecordTrackMetadata()252 static const RecordTrackMetadata& getValidRecordTrackMetadata() {
253 static const RecordTrackMetadata valid = {
254 .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT), .gain = 1};
255 return valid;
256 }
257
getValidRecordTrackMetadataWithDest()258 static const RecordTrackMetadata& getValidRecordTrackMetadataWithDest() {
259 static const RecordTrackMetadata valid = {
260 .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
261 .gain = 1,
262 .destination = [] {
263 RecordTrackMetadata::Destination dest;
264 dest.device(getValidOutputDeviceAddress());
265 return dest;
266 }()};
267 return valid;
268 }
269
getInvalidSourceRecordTrackMetadata()270 static const RecordTrackMetadata& getInvalidSourceRecordTrackMetadata() {
271 static const RecordTrackMetadata invalid = {.source = "random_string", .gain = 1};
272 return invalid;
273 }
274
getRecordTrackMetadataWithInvalidDest()275 static const RecordTrackMetadata& getRecordTrackMetadataWithInvalidDest() {
276 static const RecordTrackMetadata invalid = {
277 .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
278 .gain = 1,
279 .destination = [] {
280 RecordTrackMetadata::Destination dest;
281 dest.device(getInvalidDeviceAddress());
282 return dest;
283 }()};
284 return invalid;
285 }
286
getInvalidChannelMaskRecordTrackMetadata()287 static const RecordTrackMetadata& getInvalidChannelMaskRecordTrackMetadata() {
288 static const RecordTrackMetadata invalid = {
289 .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
290 .gain = 1,
291 .channelMask = "random_string"};
292 return invalid;
293 }
294
getInvalidTagsRecordTrackMetadata()295 static const RecordTrackMetadata& getInvalidTagsRecordTrackMetadata() {
296 static const RecordTrackMetadata invalid = {
297 .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
298 .gain = 1,
299 .tags = {{"random_string"}}};
300 return invalid;
301 }
302
getValidPlaybackTrackMetadata()303 static const PlaybackTrackMetadata& getValidPlaybackTrackMetadata() {
304 static const PlaybackTrackMetadata valid = {
305 .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
306 .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
307 .gain = 1};
308 return valid;
309 }
310
getInvalidUsagePlaybackTrackMetadata()311 static const PlaybackTrackMetadata& getInvalidUsagePlaybackTrackMetadata() {
312 static const PlaybackTrackMetadata invalid = {
313 .usage = "random_string",
314 .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
315 .gain = 1};
316 return invalid;
317 }
318
getInvalidContentTypePlaybackTrackMetadata()319 static const PlaybackTrackMetadata& getInvalidContentTypePlaybackTrackMetadata() {
320 static const PlaybackTrackMetadata invalid = {
321 .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
322 .contentType = "random_string",
323 .gain = 1};
324 return invalid;
325 }
326
getInvalidChannelMaskPlaybackTrackMetadata()327 static const PlaybackTrackMetadata& getInvalidChannelMaskPlaybackTrackMetadata() {
328 static const PlaybackTrackMetadata invalid = {
329 .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
330 .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
331 .gain = 1,
332 .channelMask = "random_string"};
333 return invalid;
334 }
335
getInvalidTagsPlaybackTrackMetadata()336 static const PlaybackTrackMetadata& getInvalidTagsPlaybackTrackMetadata() {
337 static const PlaybackTrackMetadata invalid = {
338 .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
339 .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
340 .gain = 1,
341 .tags = {{"random_string"}}};
342 return invalid;
343 }
344
getInvalidSourceMetadatas()345 static const std::vector<SourceMetadata>& getInvalidSourceMetadatas() {
346 static const std::vector<SourceMetadata> invalids = {
347 SourceMetadata{.tracks = {{getInvalidUsagePlaybackTrackMetadata()}}},
348 SourceMetadata{.tracks = {{getInvalidContentTypePlaybackTrackMetadata()}}},
349 SourceMetadata{.tracks = {{getInvalidChannelMaskPlaybackTrackMetadata()}}},
350 SourceMetadata{.tracks = {{getInvalidTagsPlaybackTrackMetadata()}}},
351 SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
352 getInvalidUsagePlaybackTrackMetadata()}}},
353 SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
354 getInvalidContentTypePlaybackTrackMetadata()}}},
355 SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
356 getInvalidChannelMaskPlaybackTrackMetadata()}}},
357 SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
358 getInvalidTagsPlaybackTrackMetadata()}}}};
359 return invalids;
360 }
getInvalidSinkMetadatas()361 static const std::vector<SinkMetadata>& getInvalidSinkMetadatas() {
362 static const std::vector<SinkMetadata> invalids = {
363 SinkMetadata{.tracks = {{getInvalidSourceRecordTrackMetadata()}}},
364 SinkMetadata{.tracks = {{getRecordTrackMetadataWithInvalidDest()}}},
365 SinkMetadata{.tracks = {{getInvalidChannelMaskRecordTrackMetadata()}}},
366 SinkMetadata{.tracks = {{getInvalidTagsRecordTrackMetadata()}}},
367 SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
368 getInvalidSourceRecordTrackMetadata()}}},
369 SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
370 getRecordTrackMetadataWithInvalidDest()}}},
371 SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
372 getInvalidChannelMaskRecordTrackMetadata()}}},
373 SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
374 getInvalidTagsRecordTrackMetadata()}}}};
375 return invalids;
376 }
377 template <typename T>
wrapMetadata(const std::vector<T> & metadata)378 static inline std::vector<SinkOrSourceMetadata> wrapMetadata(const std::vector<T>& metadata) {
379 return std::vector<SinkOrSourceMetadata>{metadata.begin(), metadata.end()};
380 }
381
TEST_P(StreamOpenTest,OpenInputOrOutputStreamTest)382 TEST_P(StreamOpenTest, OpenInputOrOutputStreamTest) {
383 doc::test(
384 "Verify that invalid arguments are rejected by "
385 "IDevice::open{Input|Output}Stream method.");
386 AudioConfig suggestedConfig{};
387 if (isParamForInputStream()) {
388 sp<::android::hardware::audio::CORE_TYPES_CPP_VERSION::IStreamIn> stream;
389 ASSERT_OK(getDevice()->openInputStream(AudioIoHandle{}, getDeviceAddress(), getConfig(),
390 getFlags(), getSinkMetadata(),
391 returnIn(res, stream, suggestedConfig)));
392 ASSERT_TRUE(stream == nullptr);
393 } else {
394 sp<::android::hardware::audio::CORE_TYPES_CPP_VERSION::IStreamOut> stream;
395 ASSERT_OK(getDevice()->openOutputStream(AudioIoHandle{}, getDeviceAddress(), getConfig(),
396 getFlags(), getSourceMetadata(),
397 returnIn(res, stream, suggestedConfig)));
398 ASSERT_TRUE(stream == nullptr);
399 }
400 EXPECT_EQ(Result::INVALID_ARGUMENTS, res);
401 EXPECT_EQ(AudioConfig{}, suggestedConfig);
402 }
403 INSTANTIATE_TEST_CASE_P(
404 InputStreamInvalidConfig, StreamOpenTest,
405 ::testing::Combine(::testing::ValuesIn(getInputDeviceInvalidConfigParameters()),
406 ::testing::Values(getValidInputDeviceAddress()),
407 ::testing::Values(SinkMetadata{
408 .tracks = {{getValidRecordTrackMetadata(),
409 getValidRecordTrackMetadataWithDest()}}})),
410 &StreamOpenParameterToString);
411 INSTANTIATE_TEST_CASE_P(
412 InputStreamInvalidAddress, StreamOpenTest,
413 ::testing::Combine(::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
414 ::testing::Values(getInvalidDeviceAddress()),
415 ::testing::Values(SinkMetadata{
416 .tracks = {{getValidRecordTrackMetadata(),
417 getValidRecordTrackMetadataWithDest()}}})),
418 &StreamOpenParameterToString);
419 INSTANTIATE_TEST_CASE_P(
420 InputStreamInvalidMetadata, StreamOpenTest,
421 ::testing::Combine(::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
422 ::testing::Values(getValidInputDeviceAddress()),
423 ::testing::ValuesIn(wrapMetadata(getInvalidSinkMetadatas()))),
424 &StreamOpenParameterToString);
425 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(StreamOpenTest);
426
427 INSTANTIATE_TEST_CASE_P(
428 OutputStreamInvalidConfig, StreamOpenTest,
429 ::testing::Combine(::testing::ValuesIn(getOutputDeviceInvalidConfigParameters()),
430 ::testing::Values(getValidOutputDeviceAddress()),
431 ::testing::Values(SourceMetadata{
432 .tracks = {{getValidPlaybackTrackMetadata()}}})),
433 &StreamOpenParameterToString);
434 INSTANTIATE_TEST_CASE_P(
435 OutputStreamInvalidAddress, StreamOpenTest,
436 ::testing::Combine(::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
437 ::testing::Values(getInvalidDeviceAddress()),
438 ::testing::Values(SourceMetadata{
439 .tracks = {{getValidPlaybackTrackMetadata()}}})),
440 &StreamOpenParameterToString);
441 INSTANTIATE_TEST_CASE_P(
442 OutputStreamInvalidMetadata, StreamOpenTest,
443 ::testing::Combine(::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
444 ::testing::Values(getValidOutputDeviceAddress()),
445 ::testing::ValuesIn(wrapMetadata(getInvalidSourceMetadatas()))),
446 &StreamOpenParameterToString);
447
448 #define TEST_SINGLE_CONFIG_IO_STREAM(test_name, documentation, code) \
449 TEST_P(SingleConfigInputStreamTest, test_name) { \
450 doc::test(documentation); \
451 code; \
452 } \
453 TEST_P(SingleConfigOutputStreamTest, test_name) { \
454 doc::test(documentation); \
455 code; \
456 }
457
testSetDevicesInvalidDeviceAddress(IStream * stream)458 static void testSetDevicesInvalidDeviceAddress(IStream* stream) {
459 ASSERT_RESULT(Result::INVALID_ARGUMENTS, stream->setDevices({getInvalidDeviceAddress()}));
460 }
461 TEST_SINGLE_CONFIG_IO_STREAM(
462 SetDevicesInvalidDeviceAddress,
463 "Verify that invalid device address is rejected by IStream::setDevices",
464 areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported")
465 : testSetDevicesInvalidDeviceAddress(stream.get()));
466
testSetAudioPropertiesInvalidArguments(IStream * stream)467 static void testSetAudioPropertiesInvalidArguments(IStream* stream) {
468 AudioConfigBaseOptional invalidFormat;
469 invalidFormat.format.value("random_string");
470 ASSERT_RESULT(invalidArgsOrNotSupported, stream->setAudioProperties(invalidFormat));
471
472 AudioConfigBaseOptional invalidChannelMask;
473 invalidChannelMask.channelMask.value("random_string");
474 ASSERT_RESULT(invalidArgsOrNotSupported, stream->setAudioProperties(invalidChannelMask));
475 }
476 TEST_SINGLE_CONFIG_IO_STREAM(
477 SetAudioPropertiesInvalidArguments,
478 "Verify that invalid arguments are rejected by IStream::setAudioProperties",
479 testSetAudioPropertiesInvalidArguments(stream.get()));
480
TEST_P(SingleConfigOutputStreamTest,UpdateInvalidSourceMetadata)481 TEST_P(SingleConfigOutputStreamTest, UpdateInvalidSourceMetadata) {
482 doc::test("Verify that invalid metadata is rejected by IStreamOut::updateSourceMetadata");
483 for (const auto& metadata : getInvalidSourceMetadatas()) {
484 ASSERT_RESULT(invalidArgsOrNotSupported, stream->updateSourceMetadata(metadata))
485 << ::testing::PrintToString(metadata);
486 }
487 }
488
TEST_P(SingleConfigInputStreamTest,UpdateInvalidSinkMetadata)489 TEST_P(SingleConfigInputStreamTest, UpdateInvalidSinkMetadata) {
490 doc::test("Verify that invalid metadata is rejected by IStreamIn::updateSinkMetadata");
491 for (const auto& metadata : getInvalidSinkMetadatas()) {
492 ASSERT_RESULT(invalidArgsOrNotSupported, stream->updateSinkMetadata(metadata))
493 << ::testing::PrintToString(metadata);
494 }
495 }
496
getOutputDevicePcmOnlyConfigParameters()497 static const std::vector<DeviceConfigParameter>& getOutputDevicePcmOnlyConfigParameters() {
498 static const std::vector<DeviceConfigParameter> parameters = [] {
499 auto allParams = getOutputDeviceConfigParameters();
500 std::vector<DeviceConfigParameter> pcmParams;
501 std::copy_if(allParams.begin(), allParams.end(), std::back_inserter(pcmParams), [](auto cfg) {
502 const auto& flags = std::get<PARAM_FLAGS>(cfg);
503 return xsd::isLinearPcm(std::get<PARAM_CONFIG>(cfg).base.format)
504 // MMAP NOIRQ and HW A/V Sync profiles use special writing protocols.
505 &&
506 std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
507 return flag == toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) ||
508 flag == toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
509 }) == flags.end();
510 });
511 return pcmParams;
512 }();
513 return parameters;
514 }
515
516 class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
517 public:
TearDown()518 void TearDown() override {
519 releasePatchIfNeeded();
520 OutputStreamTest::TearDown();
521 }
522
canQueryPresentationPosition() const523 bool canQueryPresentationPosition() const {
524 return !xsd::isTelephonyDevice(address.deviceType);
525 }
526
createPatchIfNeeded()527 void createPatchIfNeeded() {
528 if (areAudioPatchesSupported()) {
529 AudioPortConfig source;
530 source.base.format.value(getConfig().base.format);
531 source.base.sampleRateHz.value(getConfig().base.sampleRateHz);
532 source.base.channelMask.value(getConfig().base.channelMask);
533 source.ext.mix({});
534 source.ext.mix().ioHandle = helper.getIoHandle();
535 source.ext.mix().useCase.stream({});
536 AudioPortConfig sink;
537 sink.ext.device(address);
538 EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{source},
539 hidl_vec<AudioPortConfig>{sink},
540 returnIn(res, mPatchHandle)));
541 mHasPatch = res == Result::OK;
542 } else {
543 EXPECT_OK(stream->setDevices({address}));
544 }
545 }
546
releasePatchIfNeeded()547 void releasePatchIfNeeded() {
548 if (getDevice()) {
549 if (areAudioPatchesSupported() && mHasPatch) {
550 EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
551 mHasPatch = false;
552 }
553 }
554 }
555
getMixPortName() const556 const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
557
waitForPresentationPositionAdvance(StreamWriter & writer,uint64_t * firstPosition=nullptr,uint64_t * lastPosition=nullptr)558 void waitForPresentationPositionAdvance(StreamWriter& writer, uint64_t* firstPosition = nullptr,
559 uint64_t* lastPosition = nullptr) {
560 static constexpr int kWriteDurationUs = 50 * 1000;
561 static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
562 uint64_t framesInitial;
563 TimeSpec ts;
564 // Starting / resuming of streams is asynchronous at HAL level.
565 // Sometimes HAL doesn't have enough information until the audio data actually gets
566 // consumed by the hardware.
567 bool timedOut = false;
568 if (!firstPosition || *firstPosition == std::numeric_limits<uint64_t>::max()) {
569 res = Result::INVALID_STATE;
570 for (android::base::Timer elapsed;
571 res != Result::OK && !writer.hasError() &&
572 !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
573 usleep(kWriteDurationUs);
574 ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts)));
575 ASSERT_RESULT(okOrInvalidState, res);
576 }
577 ASSERT_FALSE(writer.hasError());
578 ASSERT_FALSE(timedOut);
579 } else {
580 // Use `firstPosition` instead of querying it from the HAL. This is used when
581 // `waitForPresentationPositionAdvance` is called in a loop.
582 framesInitial = *firstPosition;
583 }
584
585 uint64_t frames = framesInitial;
586 for (android::base::Timer elapsed;
587 frames <= framesInitial && !writer.hasError() &&
588 !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
589 usleep(kWriteDurationUs);
590 ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, ts)));
591 ASSERT_RESULT(Result::OK, res);
592 }
593 EXPECT_FALSE(timedOut);
594 EXPECT_FALSE(writer.hasError());
595 EXPECT_GT(frames, framesInitial);
596 if (firstPosition) *firstPosition = framesInitial;
597 if (lastPosition) *lastPosition = frames;
598 }
599
600 private:
601 AudioPatchHandle mPatchHandle = {};
602 bool mHasPatch = false;
603 };
604
TEST_P(PcmOnlyConfigOutputStreamTest,Write)605 TEST_P(PcmOnlyConfigOutputStreamTest, Write) {
606 doc::test("Check that output streams opened for PCM output accepts audio data");
607 StreamWriter writer(stream.get(), stream->getBufferSize());
608 ASSERT_TRUE(writer.start());
609 EXPECT_TRUE(writer.waitForAtLeastOneCycle());
610 }
611
TEST_P(PcmOnlyConfigOutputStreamTest,PresentationPositionAdvancesWithWrites)612 TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionAdvancesWithWrites) {
613 doc::test("Check that the presentation position advances with writes");
614 if (!canQueryPresentationPosition()) {
615 GTEST_SKIP() << "Presentation position retrieval is not possible";
616 }
617
618 ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
619 StreamWriter writer(stream.get(), stream->getBufferSize());
620 ASSERT_TRUE(writer.start());
621 ASSERT_TRUE(writer.waitForAtLeastOneCycle());
622 ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer));
623
624 writer.stop();
625 releasePatchIfNeeded();
626 }
627
TEST_P(PcmOnlyConfigOutputStreamTest,PresentationPositionPreservedOnStandby)628 TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionPreservedOnStandby) {
629 doc::test("Check that the presentation position does not reset on standby");
630 if (!canQueryPresentationPosition()) {
631 GTEST_SKIP() << "Presentation position retrieval is not possible";
632 }
633
634 ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
635 StreamWriter writer(stream.get(), stream->getBufferSize());
636 ASSERT_TRUE(writer.start());
637 ASSERT_TRUE(writer.waitForAtLeastOneCycle());
638
639 uint64_t framesInitial;
640 ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, nullptr, &framesInitial));
641 writer.pause();
642 ASSERT_OK(stream->standby());
643 writer.resume();
644
645 uint64_t frames = std::numeric_limits<uint64_t>::max();
646 ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, &frames));
647 EXPECT_GT(frames, framesInitial);
648
649 writer.stop();
650 releasePatchIfNeeded();
651 }
652
653 INSTANTIATE_TEST_CASE_P(PcmOnlyConfigOutputStream, PcmOnlyConfigOutputStreamTest,
654 ::testing::ValuesIn(getOutputDevicePcmOnlyConfigParameters()),
655 &DeviceConfigParameterToString);
656 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PcmOnlyConfigOutputStreamTest);
657
getInputDevicePcmOnlyConfigParameters()658 static const std::vector<DeviceConfigParameter>& getInputDevicePcmOnlyConfigParameters() {
659 static const std::vector<DeviceConfigParameter> parameters = [] {
660 auto allParams = getInputDeviceConfigParameters();
661 std::vector<DeviceConfigParameter> pcmParams;
662 std::copy_if(
663 allParams.begin(), allParams.end(), std::back_inserter(pcmParams), [](auto cfg) {
664 const auto& flags = std::get<PARAM_FLAGS>(cfg);
665 return xsd::isLinearPcm(std::get<PARAM_CONFIG>(cfg).base.format)
666 // MMAP NOIRQ profiles use different reading protocol,
667 // reading h/w hotword might require Soundtrigger to be active.
668 &&
669 std::find_if(
670 flags.begin(), flags.end(), [](const auto& flag) {
671 return flag == toString(
672 xsd::AudioInOutFlag::
673 AUDIO_INPUT_FLAG_MMAP_NOIRQ) ||
674 flag == toString(xsd::AudioInOutFlag::
675 AUDIO_INPUT_FLAG_HW_HOTWORD);
676 }) == flags.end();
677 });
678 return pcmParams;
679 }();
680 return parameters;
681 }
682
683 class PcmOnlyConfigInputStreamTest : public InputStreamTest {
684 public:
TearDown()685 void TearDown() override {
686 releasePatchIfNeeded();
687 InputStreamTest::TearDown();
688 }
689
canQueryCapturePosition() const690 bool canQueryCapturePosition() const {
691 // See b/263305254 and b/259636577. Must use the device initially passed in
692 // as a parameter, not 'address' which gets adjusted during test setup for
693 // the telephony case.
694 return !xsd::isTelephonyDevice(getAttachedDeviceAddress().deviceType);
695 }
696
createPatchIfNeeded()697 void createPatchIfNeeded() {
698 if (areAudioPatchesSupported()) {
699 AudioPortConfig source;
700 source.ext.device(address);
701 AudioPortConfig sink;
702 sink.base.format.value(getConfig().base.format);
703 sink.base.sampleRateHz.value(getConfig().base.sampleRateHz);
704 sink.base.channelMask.value(getConfig().base.channelMask);
705 sink.ext.mix({});
706 sink.ext.mix().ioHandle = helper.getIoHandle();
707 sink.ext.mix().useCase.source(initMetadata.tracks[0].source);
708 EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{source},
709 hidl_vec<AudioPortConfig>{sink},
710 returnIn(res, mPatchHandle)));
711 mHasPatch = res == Result::OK;
712 } else {
713 EXPECT_OK(stream->setDevices({address}));
714 }
715 }
716
releasePatchIfNeeded()717 void releasePatchIfNeeded() {
718 if (getDevice() && areAudioPatchesSupported() && mHasPatch) {
719 EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
720 mHasPatch = false;
721 }
722 }
723
waitForCapturePositionAdvance(StreamReader & reader,uint64_t * firstPosition=nullptr,uint64_t * lastPosition=nullptr)724 void waitForCapturePositionAdvance(StreamReader& reader, uint64_t* firstPosition = nullptr,
725 uint64_t* lastPosition = nullptr) {
726 static constexpr int kReadDurationUs = 50 * 1000;
727 static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
728 uint64_t framesInitial, ts;
729 // Starting / resuming of streams is asynchronous at HAL level.
730 // Sometimes HAL doesn't have enough information until the audio data actually has been
731 // produced by the hardware. Legacy HALs might return NOT_SUPPORTED when they actually
732 // mean INVALID_STATE.
733 bool timedOut = false;
734 res = Result::INVALID_STATE;
735 for (android::base::Timer elapsed;
736 res != Result::OK && !reader.hasError() &&
737 !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
738 usleep(kReadDurationUs);
739 ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
740 ASSERT_RESULT(okOrInvalidStateOrNotSupported, res);
741 }
742 ASSERT_FALSE(reader.hasError());
743 ASSERT_FALSE(timedOut);
744
745 uint64_t frames = framesInitial;
746 for (android::base::Timer elapsed;
747 frames <= framesInitial && !reader.hasError() &&
748 !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
749 usleep(kReadDurationUs);
750 ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
751 ASSERT_RESULT(Result::OK, res);
752 }
753 EXPECT_FALSE(timedOut);
754 EXPECT_FALSE(reader.hasError());
755 EXPECT_GT(frames, framesInitial);
756 if (firstPosition) *firstPosition = framesInitial;
757 if (lastPosition) *lastPosition = frames;
758 }
759
760 private:
761 AudioPatchHandle mPatchHandle = {};
762 bool mHasPatch = false;
763 };
764
TEST_P(PcmOnlyConfigInputStreamTest,Read)765 TEST_P(PcmOnlyConfigInputStreamTest, Read) {
766 doc::test("Check that input streams opened for PCM input retrieve audio data");
767 StreamReader reader(stream.get(), stream->getBufferSize());
768 ASSERT_TRUE(reader.start());
769 EXPECT_TRUE(reader.waitForAtLeastOneCycle());
770 }
771
TEST_P(PcmOnlyConfigInputStreamTest,CapturePositionAdvancesWithReads)772 TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionAdvancesWithReads) {
773 doc::test("Check that the capture position advances with reads");
774 if (!canQueryCapturePosition()) {
775 GTEST_SKIP() << "Capture position retrieval is not possible";
776 }
777
778 ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
779 StreamReader reader(stream.get(), stream->getBufferSize());
780 ASSERT_TRUE(reader.start());
781 EXPECT_TRUE(reader.waitForAtLeastOneCycle());
782 ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader));
783 }
784
TEST_P(PcmOnlyConfigInputStreamTest,CapturePositionPreservedOnStandby)785 TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionPreservedOnStandby) {
786 doc::test("Check that the capture position does not reset on standby");
787 if (!canQueryCapturePosition()) {
788 GTEST_SKIP() << "Capture position retrieval is not possible";
789 }
790
791 ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
792 StreamReader reader(stream.get(), stream->getBufferSize());
793 ASSERT_TRUE(reader.start());
794 EXPECT_TRUE(reader.waitForAtLeastOneCycle());
795
796 uint64_t framesInitial;
797 ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, nullptr, &framesInitial));
798 reader.pause();
799 ASSERT_OK(stream->standby());
800 reader.resume();
801
802 uint64_t frames;
803 ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, &frames, nullptr));
804 EXPECT_GT(frames, framesInitial);
805
806 reader.stop();
807 releasePatchIfNeeded();
808 }
809
810 INSTANTIATE_TEST_CASE_P(PcmOnlyConfigInputStream, PcmOnlyConfigInputStreamTest,
811 ::testing::ValuesIn(getInputDevicePcmOnlyConfigParameters()),
812 &DeviceConfigParameterToString);
813 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PcmOnlyConfigInputStreamTest);
814
getBuiltinMicConfigParameters()815 static const std::vector<DeviceConfigParameter>& getBuiltinMicConfigParameters() {
816 static const std::vector<DeviceConfigParameter> parameters = [] {
817 auto allParams = getInputDeviceConfigParameters();
818 std::vector<DeviceConfigParameter> builtinMicParams;
819 std::copy_if(allParams.begin(), allParams.end(), std::back_inserter(builtinMicParams),
820 [](auto cfg) {
821 // The built in mic may participate in various scenarios:
822 // FAST, HW_HOTWORD, MMAP NOIRQ, which are indicated by flags.
823 // We are only interested in testing the simplest scenario w/o any flags.
824 if (!std::get<PARAM_FLAGS>(cfg).empty()) return false;
825 auto maybeSourceDevice = getCachedPolicyConfig().getSourceDeviceForMixPort(
826 std::get<PARAM_DEVICE_NAME>(std::get<PARAM_DEVICE>(cfg)),
827 std::get<PARAM_PORT_NAME>(cfg));
828 return maybeSourceDevice.has_value() &&
829 xsd::stringToAudioDevice(maybeSourceDevice.value().deviceType) ==
830 xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC;
831 });
832 return builtinMicParams;
833 }();
834 return parameters;
835 }
836
837 class MicrophoneInfoInputStreamTest : public InputStreamTest {};
838
TEST_P(MicrophoneInfoInputStreamTest,GetActiveMicrophones)839 TEST_P(MicrophoneInfoInputStreamTest, GetActiveMicrophones) {
840 doc::test(
841 "Make sure getActiveMicrophones always succeeds when recording "
842 "from the built-in microphone.");
843 hidl_vec<MicrophoneInfo> microphones;
844 ASSERT_OK(getDevice()->getMicrophones(returnIn(res, microphones)));
845 if (res == Result::NOT_SUPPORTED) {
846 GTEST_SKIP() << "getMicrophones is not supported"; // returns
847 }
848 ASSERT_OK(res);
849
850 for (auto microphone : microphones) {
851 if (microphone.deviceAddress == address) {
852 StreamReader reader(stream.get(), stream->getBufferSize());
853 ASSERT_TRUE(reader.start());
854 reader.pause(); // This ensures that at least one read has happened.
855 EXPECT_FALSE(reader.hasError());
856
857 hidl_vec<MicrophoneInfo> activeMicrophones;
858 ASSERT_OK(stream->getActiveMicrophones(returnIn(res, activeMicrophones)));
859 ASSERT_OK(res);
860 EXPECT_NE(0U, activeMicrophones.size());
861 }
862 }
863 }
864
865 INSTANTIATE_TEST_CASE_P(MicrophoneInfoInputStream, MicrophoneInfoInputStreamTest,
866 ::testing::ValuesIn(getBuiltinMicConfigParameters()),
867 &DeviceConfigParameterToString);
868 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MicrophoneInfoInputStreamTest);
869
getOutputDeviceCompressedConfigParameters(const AudioConfigBase & configToMatch)870 static const std::vector<DeviceConfigParameter>& getOutputDeviceCompressedConfigParameters(
871 const AudioConfigBase& configToMatch) {
872 static const std::vector<DeviceConfigParameter> parameters = [&] {
873 auto allParams = getOutputDeviceConfigParameters();
874 std::vector<DeviceConfigParameter> compressedParams;
875 std::copy_if(allParams.begin(), allParams.end(), std::back_inserter(compressedParams),
876 [&](auto cfg) {
877 if (std::get<PARAM_CONFIG>(cfg).base != configToMatch) return false;
878 const auto& flags = std::get<PARAM_FLAGS>(cfg);
879 return std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
880 return flag ==
881 toString(xsd::AudioInOutFlag::
882 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
883 }) != flags.end();
884 });
885 return compressedParams;
886 }();
887 return parameters;
888 }
889
890 class CompressedOffloadOutputStreamTest : public PcmOnlyConfigOutputStreamTest {
891 public:
loadData(const std::string & fileName,std::vector<uint8_t> * data)892 void loadData(const std::string& fileName, std::vector<uint8_t>* data) {
893 std::ifstream is(fileName, std::ios::in | std::ios::binary);
894 ASSERT_TRUE(is.good()) << "Failed to open file " << fileName;
895 is.seekg(0, is.end);
896 data->reserve(data->size() + is.tellg());
897 is.seekg(0, is.beg);
898 data->insert(data->end(), std::istreambuf_iterator<char>(is),
899 std::istreambuf_iterator<char>());
900 ASSERT_TRUE(!is.fail()) << "Failed to read from file " << fileName;
901 }
902 };
903
904 class OffloadCallbacks : public IStreamOutCallback {
905 public:
onDrainReady()906 Return<void> onDrainReady() override {
907 ALOGI("onDrainReady");
908 {
909 std::lock_guard lg(mLock);
910 mOnDrainReady = true;
911 }
912 mCondVar.notify_one();
913 return {};
914 }
onWriteReady()915 Return<void> onWriteReady() override { return {}; }
onError()916 Return<void> onError() override {
917 ALOGW("onError");
918 {
919 std::lock_guard lg(mLock);
920 mOnError = true;
921 }
922 mCondVar.notify_one();
923 return {};
924 }
waitForDrainReadyOrError()925 bool waitForDrainReadyOrError() {
926 std::unique_lock l(mLock);
927 if (!mOnDrainReady && !mOnError) {
928 mCondVar.wait(l, [&]() { return mOnDrainReady || mOnError; });
929 }
930 const bool success = !mOnError;
931 mOnDrainReady = mOnError = false;
932 return success;
933 }
934
935 private:
936 std::mutex mLock;
937 bool mOnDrainReady = false;
938 bool mOnError = false;
939 std::condition_variable mCondVar;
940 };
941
TEST_P(CompressedOffloadOutputStreamTest,Mp3FormatGaplessOffload)942 TEST_P(CompressedOffloadOutputStreamTest, Mp3FormatGaplessOffload) {
943 doc::test("Check that compressed offload mix ports for MP3 implement gapless offload");
944 const auto& flags = getOutputFlags();
945 const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33;
946 // See test instantiation, only offload MP3 mix ports are used.
947 if (std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
948 return flag == toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD);
949 }) == flags.end()) {
950 if (isNewDeviceLaunchingOnTPlus) {
951 FAIL() << "New devices launching on Android T+ must support gapless offload, "
952 << "see VSR-4.3-001";
953 } else {
954 GTEST_SKIP() << "Compressed offload mix port does not support gapless offload";
955 }
956 }
957 std::vector<uint8_t> offloadData;
958 ASSERT_NO_FATAL_FAILURE(loadData("/data/local/tmp/sine882hz3s.mp3", &offloadData));
959 ASSERT_FALSE(offloadData.empty());
960 ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
961 const int presentationeEndPrecisionMs = 1000;
962 const int sampleRate = 44100;
963 // The duration of sine882hz3s.mp3 is: 3 seconds + (576 + 756) samples.
964 // This is a mono file, thus 1 frame = 1 sample for it.
965 const int fullTrackDurationMs = 3000 + (576 + 756) * 1000 / sampleRate;
966 const int significantSampleNumber = (presentationeEndPrecisionMs * sampleRate) / 1000;
967 // 'delay' is the amount of frames ignored at the beginning, 'padding' is the amount of frames
968 // ignored at the end of the track. Extra 1000 samples are requested for trimming to reduce the
969 // test running time.
970 const int delay = 576 + 1000;
971 const int padding = 756 + 1000;
972 const int durationMs = fullTrackDurationMs - (delay + padding) * 1000 / sampleRate;
973 auto start = std::chrono::steady_clock::now();
974 auto callbacks = sp<OffloadCallbacks>::make();
975 std::mutex presentationEndLock;
976 std::vector<float> presentationEndTimes;
977 // StreamWriter plays 'offloadData' in a loop, possibly using multiple calls to 'write', this
978 // depends on the relative sizes of 'offloadData' and the HAL buffer. Writer calls 'onDataStart'
979 // each time it starts writing the buffer from the beginning, and 'onDataWrap' callback each
980 // time it wraps around the buffer.
981 StreamWriter writer(
982 stream.get(), stream->getBufferSize(), std::move(offloadData),
983 [&]() /* onDataStart */ { start = std::chrono::steady_clock::now(); },
984 [&]() /* onDataWrap */ {
985 Return<Result> ret(Result::OK);
986 // Decrease the volume since the test plays a loud sine wave.
987 ret = stream->setVolume(0.1, 0.1);
988 if (!ret.isOk() || ret != Result::OK) {
989 ALOGE("%s: setVolume failed: %s", __func__, toString(ret).c_str());
990 return false;
991 }
992 ret = Parameters::set(
993 stream, {{AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, std::to_string(delay)},
994 {AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, std::to_string(padding)}});
995 if (!ret.isOk() || ret != Result::OK) {
996 ALOGE("%s: setParameters failed: %s", __func__, toString(ret).c_str());
997 return false;
998 }
999 ret = stream->drain(AudioDrain::EARLY_NOTIFY);
1000 if (!ret.isOk() || ret != Result::OK) {
1001 ALOGE("%s: drain failed: %s", __func__, toString(ret).c_str());
1002 return false;
1003 }
1004 // FIXME: On some SoCs intermittent errors are possible, ignore them.
1005 if (callbacks->waitForDrainReadyOrError()) {
1006 const float duration = std::chrono::duration_cast<std::chrono::milliseconds>(
1007 std::chrono::steady_clock::now() - start)
1008 .count();
1009 std::lock_guard lg(presentationEndLock);
1010 presentationEndTimes.push_back(duration);
1011 }
1012 return true;
1013 });
1014 ASSERT_OK(stream->setCallback(callbacks));
1015 ASSERT_TRUE(writer.start());
1016 // How many times to loop the track so that the sum of gapless delay and padding from
1017 // the first presentation end to the last is at least 'presentationeEndPrecisionMs'.
1018 const int playbackNumber = (int)(significantSampleNumber / ((float)delay + padding) + 1);
1019 for (bool done = false; !done;) {
1020 usleep(presentationeEndPrecisionMs * 1000);
1021 {
1022 std::lock_guard lg(presentationEndLock);
1023 done = presentationEndTimes.size() >= playbackNumber;
1024 }
1025 ASSERT_FALSE(writer.hasError()) << "Recent write or drain operation has failed";
1026 }
1027 const float avgDuration =
1028 std::accumulate(presentationEndTimes.begin(), presentationEndTimes.end(), 0.0) /
1029 presentationEndTimes.size();
1030 std::stringstream observedEndTimes;
1031 std::copy(presentationEndTimes.begin(), presentationEndTimes.end(),
1032 std::ostream_iterator<float>(observedEndTimes, ", "));
1033 EXPECT_NEAR(durationMs, avgDuration, presentationeEndPrecisionMs * 0.1)
1034 << "Observed durations: " << observedEndTimes.str();
1035 writer.stop();
1036 EXPECT_OK(stream->clearCallback());
1037 releasePatchIfNeeded();
1038 }
1039
1040 INSTANTIATE_TEST_CASE_P(
1041 CompressedOffloadOutputStream, CompressedOffloadOutputStreamTest,
1042 ::testing::ValuesIn(getOutputDeviceCompressedConfigParameters(AudioConfigBase{
1043 .format = xsd::toString(xsd::AudioFormat::AUDIO_FORMAT_MP3),
1044 .sampleRateHz = 44100,
1045 .channelMask = xsd::toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO)})),
1046 &DeviceConfigParameterToString);
1047 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompressedOffloadOutputStreamTest);
1048