• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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