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 /**
18 * Test whether an error callback is joined before the close finishes.
19 *
20 * Start a stream with a callback.
21 * The callback just sleeps for a long time.
22 * While the callback is sleeping, close() the stream from the main thread.
23 * Then check to make sure the callback was joined before the close() returns.
24 *
25 * This can hang if there are deadlocks. So make sure you get a PASSED result.
26 */
27
28 #include <atomic>
29 #include <stdio.h>
30 #include <unistd.h>
31
32 #include <aaudio/AAudio.h>
33
34 // Sleep long enough that the foreground has a chance to call close.
35 static constexpr int kCallbackSleepMillis = 1000;
36 static constexpr int kPollSleepMillis = 100;
37
38 static int sErrorCount = 0;
39
40 #define MY_ASSERT_TRUE(statement) \
41 if (!(statement)) { \
42 printf("ERROR line:%d - " #statement "\n", __LINE__); \
43 sErrorCount++; \
44 return false; \
45 }
46
47 #define MY_ASSERT_EQ(aa,bb) MY_ASSERT_TRUE(((aa) == (bb)))
48 #define MY_ASSERT_NE(aa,bb) MY_ASSERT_TRUE(((aa) != (bb)))
49
50 class AudioEngine {
51 public:
52
53 // Check for a crash or late callback if we close without stopping.
checkCloseJoins(aaudio_direction_t direction,aaudio_performance_mode_t perfMode,bool callStopFromCallback)54 bool checkCloseJoins(aaudio_direction_t direction,
55 aaudio_performance_mode_t perfMode,
56 bool callStopFromCallback) {
57 mCallStopFromCallback = callStopFromCallback;
58
59 if (!startStreamForStall(direction, perfMode)) return false;
60
61 printf("--------------------------------------------------------\n");
62 printf("%s() - direction = %d, perfMode = %d, callStop = %d\n",
63 __func__, direction, perfMode, callStopFromCallback);
64
65 // When the callback starts it will go to sleep.
66 if (!waitForCallbackToStart()) return false;
67
68 printf("call AAudioStream_close()\n");
69 MY_ASSERT_TRUE(!mCallbackFinished); // Still sleeping?
70 aaudio_result_t result = AAudioStream_close(mStream); // May hang here!
71 if (mCallbackStarted) {
72 MY_ASSERT_TRUE(mCallbackFinished);
73 }
74 MY_ASSERT_EQ(AAUDIO_OK, result);
75 printf("AAudioStream_close() returned %d\n", result);
76
77 MY_ASSERT_EQ(AAUDIO_ERROR_DISCONNECTED, mError.load());
78 if (mCallStopFromCallback) {
79 // Did calling stop() from callback fail? It should have.
80 MY_ASSERT_NE(AAUDIO_OK, mStopResult.load());
81 }
82
83 return true;
84 }
85
86 private:
startStreamForStall(aaudio_direction_t direction,aaudio_performance_mode_t perfMode)87 bool startStreamForStall(aaudio_direction_t direction,
88 aaudio_performance_mode_t perfMode) {
89 AAudioStreamBuilder* builder = nullptr;
90 aaudio_result_t result = AAUDIO_OK;
91
92 // Use an AAudioStreamBuilder to contain requested parameters.
93 result = AAudio_createStreamBuilder(&builder);
94 MY_ASSERT_EQ(AAUDIO_OK, result);
95
96 // Request stream properties.
97 AAudioStreamBuilder_setDirection(builder, direction);
98 AAudioStreamBuilder_setPerformanceMode(builder, perfMode);
99 AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
100 AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
101
102 // Create an AAudioStream using the Builder.
103 result = AAudioStreamBuilder_openStream(builder, &mStream);
104 AAudioStreamBuilder_delete(builder);
105 MY_ASSERT_EQ(AAUDIO_OK, result);
106
107 // Check to see what kind of stream we actually got.
108 int32_t deviceId = AAudioStream_getDeviceId(mStream);
109 aaudio_performance_mode_t
110 actualPerfMode = AAudioStream_getPerformanceMode(mStream);
111 printf("-------- opened: deviceId = %3d, perfMode = %d\n",
112 deviceId,
113 actualPerfMode);
114
115 // Start stream.
116 result = AAudioStream_requestStart(mStream);
117 MY_ASSERT_EQ(AAUDIO_OK, result);
118
119 return true;
120 }
121
waitForCallbackToStart()122 bool waitForCallbackToStart() {
123 // Wait for callback to say it has been called.
124 int countDown = 10 * 1000 / kPollSleepMillis;
125 while (!mCallbackStarted && countDown > 0) {
126 if ((countDown % 5) == 0) {
127 printf("===== Please PLUG or UNPLUG headphones! ======= %d\n", countDown);
128 }
129 usleep(kPollSleepMillis * 1000);
130 countDown--;
131 }
132 MY_ASSERT_TRUE(countDown > 0);
133 MY_ASSERT_TRUE(mCallbackStarted);
134 return true;
135 }
136
137 // Callback function that fills the audio output buffer.
s_myDataCallbackProc(AAudioStream *,void *,void *,int32_t)138 static aaudio_data_callback_result_t s_myDataCallbackProc(
139 AAudioStream * /* stream */,
140 void * /* userData */,
141 void * /* audioData */,
142 int32_t /* numFrames */
143 ) {
144 return AAUDIO_CALLBACK_RESULT_CONTINUE;
145 }
146
s_myErrorCallbackProc(AAudioStream * stream,void * userData,aaudio_result_t error)147 static void s_myErrorCallbackProc(
148 AAudioStream * stream,
149 void *userData,
150 aaudio_result_t error) {
151 AudioEngine *engine = (AudioEngine *)userData;
152 engine->mError = error;
153 engine->mCallbackStarted = true;
154 usleep(kCallbackSleepMillis * 1000);
155 // it is illegal to call stop() from the callback. It should
156 // return an error and not hang.
157 if (engine->mCallStopFromCallback) {
158 engine->mStopResult = AAudioStream_requestStop(stream);
159 }
160 engine->mCallbackFinished = true;
161 }
162
163 AAudioStream* mStream = nullptr;
164
165 std::atomic<aaudio_result_t> mError{AAUDIO_OK}; // written by error callback
166 std::atomic<bool> mCallStopFromCallback{false};
167 std::atomic<bool> mCallbackStarted{false}; // written by error callback
168 std::atomic<bool> mCallbackFinished{false}; // written by error callback
169 std::atomic<aaudio_result_t> mStopResult{AAUDIO_OK};
170 };
171
main(int,char **)172 int main(int, char **) {
173 // Parameters to test.
174 static aaudio_direction_t directions[] = {AAUDIO_DIRECTION_OUTPUT,
175 AAUDIO_DIRECTION_INPUT};
176 static aaudio_performance_mode_t perfModes[] =
177 {AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_PERFORMANCE_MODE_NONE};
178 static bool callStops[] = { false, true };
179
180 // Make printf print immediately so that debug info is not stuck
181 // in a buffer if we hang or crash.
182 setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
183
184 printf("Test Disconnect Race V1.0\n");
185 printf("\n");
186
187 for (auto callStop : callStops) {
188 for (auto direction : directions) {
189 for (auto perfMode : perfModes) {
190 AudioEngine engine;
191 engine.checkCloseJoins(direction, perfMode, callStop);
192 }
193 }
194 }
195
196 printf("Error Count = %d, %s\n", sErrorCount,
197 ((sErrorCount == 0) ? "PASS" : "FAIL"));
198 }
199