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 EXPECT_TRUE(checkPatchPlayback(cb->mAudioIo, cb->mDeviceId));
68 EXPECT_NE(0, ap->getAudioTrackHandle()->getFlags() & output_flags[i]);
69 audio_patch patch;
70 EXPECT_EQ(OK, getPatchForOutputMix(cb->mAudioIo, patch));
71 if (output_flags[i] != AUDIO_OUTPUT_FLAG_FAST) {
72 // A "normal" output can still have a FastMixer, depending on the buffer size.
73 // Thus, a fast track can be created on a mix port which does not have the FAST flag.
74 for (auto j = 0; j < patch.num_sources; j++) {
75 if (patch.sources[j].type == AUDIO_PORT_TYPE_MIX &&
76 patch.sources[j].ext.mix.handle == cb->mAudioIo) {
77 SCOPED_TRACE(dumpPortConfig(patch.sources[j]));
78 EXPECT_NE(0, patch.sources[j].flags.output & output_flags[i])
79 << "expected output flag "
80 << audio_output_flag_to_string(output_flags[i]) << " is absent";
81 }
82 }
83 }
84 ap->stop();
85 }
86 }
87
TEST(AudioTrackTest,DefaultRoutingTest)88 TEST(AudioTrackTest, DefaultRoutingTest) {
89 audio_port_v7 port;
90 if (OK != getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
91 AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port)) {
92 GTEST_SKIP() << "remote submix in device not connected";
93 }
94
95 // create record instance
96 sp<AudioCapture> capture = sp<AudioCapture>::make(
97 AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
98 ASSERT_NE(nullptr, capture);
99 ASSERT_EQ(OK, capture->create()) << "record creation failed";
100 sp<OnAudioDeviceUpdateNotifier> cbCapture = sp<OnAudioDeviceUpdateNotifier>::make();
101 EXPECT_EQ(OK, capture->getAudioRecordHandle()->addAudioDeviceCallback(cbCapture));
102
103 // create playback instance
104 sp<AudioPlayback> playback = sp<AudioPlayback>::make(
105 48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
106 AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE);
107 ASSERT_NE(nullptr, playback);
108 ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
109 << "Unable to open Resource";
110 ASSERT_EQ(OK, playback->create()) << "track creation failed";
111 sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
112 EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
113
114 // capture should be routed to submix in port
115 EXPECT_EQ(OK, capture->start()) << "start recording failed";
116 EXPECT_EQ(OK, cbCapture->waitForAudioDeviceCb());
117 EXPECT_EQ(port.id, capture->getAudioRecordHandle()->getRoutedDeviceId())
118 << "Capture NOT routed on expected port";
119
120 // capture start should create submix out port
121 status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
122 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
123 EXPECT_EQ(OK, status) << "Could not find port";
124
125 // playback should be routed to submix out as long as capture is active
126 EXPECT_EQ(OK, playback->start()) << "audio track start failed";
127 EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
128 EXPECT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
129 << "Playback NOT routed on expected port";
130
131 capture->stop();
132 playback->stop();
133 }
134
135 class AudioRoutingTest : public ::testing::Test {
136 public:
SetUp()137 void SetUp() override {
138 audio_port_v7 port;
139 if (OK != getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
140 AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port)) {
141 GTEST_SKIP() << "remote submix in device not connected";
142 }
143 uint32_t mixType = MIX_TYPE_PLAYERS;
144 uint32_t mixFlag = MIX_ROUTE_FLAG_LOOP_BACK;
145 audio_devices_t deviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
146 AudioMixMatchCriterion criterion(AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT,
147 RULE_MATCH_ATTRIBUTE_USAGE);
148 std::vector<AudioMixMatchCriterion> criteria{criterion};
149 audio_config_t config = AUDIO_CONFIG_INITIALIZER;
150 config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
151 config.format = AUDIO_FORMAT_PCM_16_BIT;
152 config.sample_rate = 48000;
153 AudioMix mix(criteria, mixType, config, mixFlag, String8{mAddress.c_str()}, 0);
154 mix.mDeviceType = deviceType;
155 mix.mToken = sp<BBinder>::make();
156 mMixes.push(mix);
157 if (OK == AudioSystem::registerPolicyMixes(mMixes, true)) {
158 mPolicyMixRegistered = true;
159 }
160 ASSERT_TRUE(mPolicyMixRegistered) << "register policy mix failed";
161 }
162
TearDown()163 void TearDown() override {
164 if (mPolicyMixRegistered) {
165 EXPECT_EQ(OK, AudioSystem::registerPolicyMixes(mMixes, false));
166 }
167 }
168
169 bool mPolicyMixRegistered{false};
170 std::string mAddress{"mix_1"};
171 Vector<AudioMix> mMixes;
172 };
173
TEST_F(AudioRoutingTest,ConcurrentDynamicRoutingTest)174 TEST_F(AudioRoutingTest, ConcurrentDynamicRoutingTest) {
175 audio_port_v7 port, port_mix;
176 // expect legacy submix in port to be connected
177 status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
178 AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port);
179 EXPECT_EQ(OK, status) << "Could not find port";
180
181 // as policy mix is registered, expect submix in port with mAddress to be connected
182 status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
183 AUDIO_DEVICE_IN_REMOTE_SUBMIX, mAddress, port_mix);
184 EXPECT_EQ(OK, status) << "Could not find port";
185
186 // create playback instance
187 sp<AudioPlayback> playback = sp<AudioPlayback>::make(
188 48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
189 AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN);
190 ASSERT_NE(nullptr, playback);
191 ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
192 << "Unable to open Resource";
193 ASSERT_EQ(OK, playback->create()) << "track creation failed";
194 sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
195 EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
196
197 // create capture instances on different ports
198 sp<AudioCapture> captureA = sp<AudioCapture>::make(
199 AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
200 ASSERT_NE(nullptr, captureA);
201 ASSERT_EQ(OK, captureA->create()) << "record creation failed";
202 sp<OnAudioDeviceUpdateNotifier> cbCaptureA = sp<OnAudioDeviceUpdateNotifier>::make();
203 EXPECT_EQ(OK, captureA->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureA));
204
205 audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
206 attr.source = AUDIO_SOURCE_REMOTE_SUBMIX;
207 sprintf(attr.tags, "addr=%s", mAddress.c_str());
208 sp<AudioCapture> captureB = sp<AudioCapture>::make(
209 AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
210 AUDIO_INPUT_FLAG_NONE, AUDIO_SESSION_ALLOCATE, AudioRecord::TRANSFER_CALLBACK, &attr);
211 ASSERT_NE(nullptr, captureB);
212 ASSERT_EQ(OK, captureB->create()) << "record creation failed";
213 sp<OnAudioDeviceUpdateNotifier> cbCaptureB = sp<OnAudioDeviceUpdateNotifier>::make();
214 EXPECT_EQ(OK, captureB->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureB));
215
216 // launch
217 EXPECT_EQ(OK, captureA->start()) << "start recording failed";
218 EXPECT_EQ(OK, cbCaptureA->waitForAudioDeviceCb());
219 EXPECT_EQ(port.id, captureA->getAudioRecordHandle()->getRoutedDeviceId())
220 << "Capture NOT routed on expected port";
221
222 EXPECT_EQ(OK, captureB->start()) << "start recording failed";
223 EXPECT_EQ(OK, cbCaptureB->waitForAudioDeviceCb());
224 EXPECT_EQ(port_mix.id, captureB->getAudioRecordHandle()->getRoutedDeviceId())
225 << "Capture NOT routed on expected port";
226
227 // as record started, expect submix out ports to be connected
228 status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
229 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
230 EXPECT_EQ(OK, status) << "unexpected submix out port found";
231
232 status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
233 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mAddress, port_mix);
234 EXPECT_EQ(OK, status) << "Could not find port";
235
236 // check if playback routed to desired port
237 EXPECT_EQ(OK, playback->start());
238 EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
239 EXPECT_EQ(port_mix.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
240 << "Playback NOT routed on expected port";
241
242 captureB->stop();
243
244 // check if mAddress submix out is disconnected as capture session is stopped
245 status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
246 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mAddress, port_mix);
247 EXPECT_NE(OK, status) << "unexpected submix in port found";
248
249 // check if legacy submix out is connected
250 status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
251 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
252 EXPECT_EQ(OK, status) << "port not found";
253
254 // unregister policy
255 EXPECT_EQ(OK, AudioSystem::registerPolicyMixes(mMixes, false));
256 mPolicyMixRegistered = false;
257
258 // as policy mix is unregistered, expect submix in port with mAddress to be disconnected
259 status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
260 AUDIO_DEVICE_IN_REMOTE_SUBMIX, mAddress, port_mix);
261 EXPECT_NE(OK, status) << "unexpected submix in port found";
262
263 playback->onProcess();
264 // as captureA is active, it should re route to legacy submix
265 EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb(port.id));
266 EXPECT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
267 << "Playback NOT routed on expected port";
268
269 captureA->stop();
270 playback->stop();
271 }
272
main(int argc,char ** argv)273 int main(int argc, char** argv) {
274 android::ProcessState::self()->startThreadPool();
275 ::testing::InitGoogleTest(&argc, argv);
276 ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
277 return RUN_ALL_TESTS();
278 }
279