xref: /aosp_15_r20/frameworks/av/media/libaudioclient/tests/audiorouting_tests.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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