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