1 /*
2 * Copyright (C) 2021 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "AudioRoutingTest"
19
20 #include <string.h>
21
22 #include <binder/Binder.h>
23 #include <binder/ProcessState.h>
24 #include <cutils/properties.h>
25 #include <gtest/gtest.h>
26
27 #include "audio_test_utils.h"
28 #include "test_execution_tracer.h"
29
30 using namespace android;
31
32 // UNIT TEST
TEST(AudioTrackTest,TestPerformanceMode)33 TEST(AudioTrackTest, TestPerformanceMode) {
34 std::vector<struct audio_port_v7> ports;
35 ASSERT_EQ(OK, listAudioPorts(ports));
36 audio_output_flags_t output_flags[] = {AUDIO_OUTPUT_FLAG_FAST, AUDIO_OUTPUT_FLAG_DEEP_BUFFER};
37 audio_flags_mask_t flags[] = {AUDIO_FLAG_LOW_LATENCY, AUDIO_FLAG_DEEP_BUFFER};
38 bool hasFlag = false;
39 for (int i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
40 hasFlag = false;
41 for (const auto& port : ports) {
42 if (port.role == AUDIO_PORT_ROLE_SOURCE && port.type == AUDIO_PORT_TYPE_MIX) {
43 if ((port.active_config.flags.output & output_flags[i]) != 0) {
44 hasFlag = true;
45 break;
46 }
47 }
48 }
49 if (!hasFlag) continue;
50 audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
51 attributes.usage = AUDIO_USAGE_MEDIA;
52 attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
53 attributes.flags = flags[i];
54 sp<AudioPlayback> ap = sp<AudioPlayback>::make(0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
55 AUDIO_CHANNEL_OUT_STEREO,
56 AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
57 AudioTrack::TRANSFER_OBTAIN, &attributes);
58 ASSERT_NE(nullptr, ap);
59 ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
60 << "Unable to open Resource";
61 ASSERT_EQ(OK, ap->create()) << "track creation failed";
62 sp<OnAudioDeviceUpdateNotifier> cb = sp<OnAudioDeviceUpdateNotifier>::make();
63 EXPECT_EQ(OK, ap->getAudioTrackHandle()->addAudioDeviceCallback(cb));
64 EXPECT_EQ(OK, ap->start()) << "audio track start failed";
65 EXPECT_EQ(OK, ap->onProcess());
66 EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
67 const auto [audioIo, deviceIds] = cb->getLastPortAndDevices();
68 EXPECT_TRUE(checkPatchPlayback(audioIo, deviceIds));
69 EXPECT_NE(0, ap->getAudioTrackHandle()->getFlags() & output_flags[i]);
70 audio_patch patch;
71 EXPECT_EQ(OK, getPatchForOutputMix(audioIo, patch));
72 if (output_flags[i] != AUDIO_OUTPUT_FLAG_FAST) {
73 // A "normal" output can still have a FastMixer, depending on the buffer size.
74 // Thus, a fast track can be created on a mix port which does not have the FAST flag.
75 for (auto j = 0; j < patch.num_sources; j++) {
76 if (patch.sources[j].type == AUDIO_PORT_TYPE_MIX &&
77 patch.sources[j].ext.mix.handle == audioIo) {
78 SCOPED_TRACE(dumpPortConfig(patch.sources[j]));
79 EXPECT_NE(0, patch.sources[j].flags.output & output_flags[i])
80 << "expected output flag "
81 << audio_output_flag_to_string(output_flags[i]) << " is absent";
82 }
83 }
84 }
85 ap->stop();
86 }
87 }
88
89 class AudioTrackTest
90 : public ::testing::TestWithParam<int> {
91
92 public:
AudioTrackTest()93 AudioTrackTest()
94 : mSampleRate(GetParam()){};
95
96 const uint32_t mSampleRate;
97
98 };
99
TEST_P(AudioTrackTest,DefaultRoutingTest)100 TEST_P(AudioTrackTest, DefaultRoutingTest) {
101 audio_port_v7 port;
102 if (OK != getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
103 AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port)) {
104 GTEST_SKIP() << "remote submix in device not connected";
105 }
106
107 // create record instance
108 sp<AudioCapture> capture = sp<AudioCapture>::make(
109 AUDIO_SOURCE_REMOTE_SUBMIX, mSampleRate, AUDIO_FORMAT_PCM_16_BIT,
110 AUDIO_CHANNEL_IN_STEREO);
111 ASSERT_NE(nullptr, capture);
112 ASSERT_EQ(OK, capture->create()) << "record creation failed";
113 sp<OnAudioDeviceUpdateNotifier> cbCapture = sp<OnAudioDeviceUpdateNotifier>::make();
114 EXPECT_EQ(OK, capture->getAudioRecordHandle()->addAudioDeviceCallback(cbCapture));
115
116 // create playback instance
117 sp<AudioPlayback> playback = sp<AudioPlayback>::make(
118 mSampleRate, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
119 AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE);
120 ASSERT_NE(nullptr, playback);
121 ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
122 << "Unable to open Resource";
123 ASSERT_EQ(OK, playback->create()) << "track creation failed";
124 sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
125 EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
126
127 // capture should be routed to submix in port
128 EXPECT_EQ(OK, capture->start()) << "start recording failed";
129 EXPECT_EQ(OK, cbCapture->waitForAudioDeviceCb());
130 DeviceIdVector routedDeviceIds = capture->getAudioRecordHandle()->getRoutedDeviceIds();
131 EXPECT_EQ(port.id, routedDeviceIds[0]) << "Capture NOT routed on expected port";
132
133 // capture start should create submix out port
134 status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
135 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
136 EXPECT_EQ(OK, status) << "Could not find port";
137
138 // playback should be routed to submix out as long as capture is active
139 EXPECT_EQ(OK, playback->start()) << "audio track start failed";
140 EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
141 routedDeviceIds = playback->getAudioTrackHandle()->getRoutedDeviceIds();
142 EXPECT_EQ(port.id, routedDeviceIds[0]) << "Playback NOT routed on expected port";
143
144 capture->stop();
145 playback->stop();
146 }
147
148 INSTANTIATE_TEST_SUITE_P(
149 AudioTrackParameterizedTest,
150 AudioTrackTest,
151 ::testing::Values(44100, 48000)
152 );
153
154 class AudioRoutingTest : public ::testing::Test {
155 public:
SetUp()156 void SetUp() override {
157 audio_port_v7 port;
158 if (OK != getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
159 AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port)) {
160 GTEST_SKIP() << "remote submix in device not connected";
161 }
162 uint32_t mixType = MIX_TYPE_PLAYERS;
163 uint32_t mixFlag = MIX_ROUTE_FLAG_LOOP_BACK;
164 audio_devices_t deviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
165 AudioMixMatchCriterion criterion(AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT,
166 RULE_MATCH_ATTRIBUTE_USAGE);
167 std::vector<AudioMixMatchCriterion> criteria{criterion};
168 audio_config_t config = AUDIO_CONFIG_INITIALIZER;
169 config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
170 config.format = AUDIO_FORMAT_PCM_16_BIT;
171 config.sample_rate = 48000;
172 AudioMix mix(criteria, mixType, config, mixFlag, String8{mAddress.c_str()}, 0);
173 mix.mDeviceType = deviceType;
174 mix.mToken = sp<BBinder>::make();
175 mMixes.push(mix);
176 if (OK == AudioSystem::registerPolicyMixes(mMixes, true)) {
177 mPolicyMixRegistered = true;
178 }
179 ASSERT_TRUE(mPolicyMixRegistered) << "register policy mix failed";
180 }
181
TearDown()182 void TearDown() override {
183 if (mPolicyMixRegistered) {
184 EXPECT_EQ(OK, AudioSystem::registerPolicyMixes(mMixes, false));
185 }
186 }
187
188 bool mPolicyMixRegistered{false};
189 std::string mAddress{"mix_1"};
190 Vector<AudioMix> mMixes;
191 };
192
TEST_F(AudioRoutingTest,ConcurrentDynamicRoutingTest)193 TEST_F(AudioRoutingTest, ConcurrentDynamicRoutingTest) {
194 audio_port_v7 port, port_mix;
195 // expect legacy submix in port to be connected
196 status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
197 AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port);
198 EXPECT_EQ(OK, status) << "Could not find port";
199
200 // as policy mix is registered, expect submix in port with mAddress to be connected
201 status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
202 AUDIO_DEVICE_IN_REMOTE_SUBMIX, mAddress, port_mix);
203 EXPECT_EQ(OK, status) << "Could not find port";
204
205 // create playback instance
206 sp<AudioPlayback> playback = sp<AudioPlayback>::make(
207 48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
208 AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN);
209 ASSERT_NE(nullptr, playback);
210 ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
211 << "Unable to open Resource";
212 ASSERT_EQ(OK, playback->create()) << "track creation failed";
213 sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
214 EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
215
216 // create capture instances on different ports
217 sp<AudioCapture> captureA = sp<AudioCapture>::make(
218 AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
219 ASSERT_NE(nullptr, captureA);
220 ASSERT_EQ(OK, captureA->create()) << "record creation failed";
221 sp<OnAudioDeviceUpdateNotifier> cbCaptureA = sp<OnAudioDeviceUpdateNotifier>::make();
222 EXPECT_EQ(OK, captureA->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureA));
223
224 audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
225 attr.source = AUDIO_SOURCE_REMOTE_SUBMIX;
226 sprintf(attr.tags, "addr=%s", mAddress.c_str());
227 sp<AudioCapture> captureB = sp<AudioCapture>::make(
228 AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
229 AUDIO_INPUT_FLAG_NONE, AUDIO_SESSION_ALLOCATE, AudioRecord::TRANSFER_CALLBACK, &attr);
230 ASSERT_NE(nullptr, captureB);
231 ASSERT_EQ(OK, captureB->create()) << "record creation failed";
232 sp<OnAudioDeviceUpdateNotifier> cbCaptureB = sp<OnAudioDeviceUpdateNotifier>::make();
233 EXPECT_EQ(OK, captureB->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureB));
234
235 // launch
236 EXPECT_EQ(OK, captureA->start()) << "start recording failed";
237 EXPECT_EQ(OK, cbCaptureA->waitForAudioDeviceCb());
238 DeviceIdVector routedDeviceIds = captureA->getAudioRecordHandle()->getRoutedDeviceIds();
239 EXPECT_EQ(port.id, routedDeviceIds[0]) << "Capture NOT routed on expected port";
240
241 EXPECT_EQ(OK, captureB->start()) << "start recording failed";
242 EXPECT_EQ(OK, cbCaptureB->waitForAudioDeviceCb());
243 routedDeviceIds = captureB->getAudioRecordHandle()->getRoutedDeviceIds();
244 EXPECT_EQ(port_mix.id, routedDeviceIds[0]) << "Capture NOT routed on expected port";
245
246 // as record started, expect submix out ports to be connected
247 status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
248 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
249 EXPECT_EQ(OK, status) << "unexpected submix out port found";
250
251 status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
252 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mAddress, port_mix);
253 EXPECT_EQ(OK, status) << "Could not find port";
254
255 // check if playback routed to desired port
256 EXPECT_EQ(OK, playback->start());
257 EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
258 routedDeviceIds = playback->getAudioTrackHandle()->getRoutedDeviceIds();
259 EXPECT_EQ(port_mix.id, routedDeviceIds[0]) << "Playback NOT routed on expected port";
260
261 captureB->stop();
262
263 // check if mAddress submix out is disconnected as capture session is stopped
264 status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
265 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mAddress, port_mix);
266 EXPECT_NE(OK, status) << "unexpected submix in port found";
267
268 // check if legacy submix out is connected
269 status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
270 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
271 EXPECT_EQ(OK, status) << "port not found";
272
273 // unregister policy
274 EXPECT_EQ(OK, AudioSystem::registerPolicyMixes(mMixes, false));
275 mPolicyMixRegistered = false;
276
277 // as policy mix is unregistered, expect submix in port with mAddress to be disconnected
278 status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
279 AUDIO_DEVICE_IN_REMOTE_SUBMIX, mAddress, port_mix);
280 EXPECT_NE(OK, status) << "unexpected submix in port found";
281
282 playback->onProcess();
283 // as captureA is active, it should re route to legacy submix
284 EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb(port.id));
285 routedDeviceIds = playback->getAudioTrackHandle()->getRoutedDeviceIds();
286 EXPECT_EQ(port.id, routedDeviceIds[0]) << "Playback NOT routed on expected port";
287
288 captureA->stop();
289 playback->stop();
290 }
291
main(int argc,char ** argv)292 int main(int argc, char** argv) {
293 android::ProcessState::self()->startThreadPool();
294 ::testing::InitGoogleTest(&argc, argv);
295 ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
296 return RUN_ALL_TESTS();
297 }
298