Merge "aaudio test: trigger race condition and hang" into qt-dev
am: 470213eb4e

Change-Id: Ieb73926a90a7d62f537db9288fb9282fac20bc5d
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 958bb2e..6101e99 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -198,6 +198,18 @@
 }
 
 cc_test {
+    name: "test_stop_hang",
+    defaults: ["libaaudio_tests_defaults"],
+    srcs: ["test_stop_hang.cpp"],
+    shared_libs: [
+        "libaaudio",
+        "libbinder",
+        "libcutils",
+        "libutils",
+    ],
+}
+
+cc_test {
     name: "test_full_queue",
     defaults: ["libaaudio_tests_defaults"],
     srcs: ["test_full_queue.cpp"],
diff --git a/media/libaaudio/tests/test_stop_hang.cpp b/media/libaaudio/tests/test_stop_hang.cpp
new file mode 100644
index 0000000..2397b6c
--- /dev/null
+++ b/media/libaaudio/tests/test_stop_hang.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Return stop from the callback
+ * and then close the stream immediately.
+ */
+
+#include <atomic>
+#include <mutex>
+#include <stdio.h>
+#include <thread>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+
+#define DURATION_SECONDS   5
+
+struct AudioEngine {
+    AAudioStreamBuilder *builder = nullptr;
+    AAudioStream        *stream = nullptr;
+    std::thread         *thread = nullptr;
+
+    std::atomic<bool>   started{false};
+    std::mutex          doneLock; // Use a mutex so we can sleep on it while join()ing.
+    std::atomic<bool>   done{false};
+
+    aaudio_result_t join() {
+        aaudio_result_t result = AAUDIO_ERROR_INVALID_STATE;
+        if (stream != nullptr) {
+            while (true) {
+                {
+                    // Will block if the thread is running.
+                    // This mutex is used to close() immediately after the callback returns
+                    // and before the requestStop() is called.
+                    std::lock_guard<std::mutex> lock(doneLock);
+                    if (done) break;
+                }
+                printf("join() got mutex but stream not done!");
+                usleep(10 * 1000); // sleep then check again
+            }
+            result = AAudioStream_close(stream);
+            stream = nullptr;
+        }
+        return result;
+    }
+};
+
+// Callback function that fills the audio output buffer.
+static aaudio_data_callback_result_t s_myDataCallbackProc(
+        AAudioStream *stream,
+        void *userData,
+        void *audioData,
+        int32_t numFrames
+) {
+    (void) stream;
+    (void) audioData;
+    (void) numFrames;
+    AudioEngine *engine = (struct AudioEngine *)userData;
+    std::lock_guard<std::mutex> lock(engine->doneLock);
+    engine->started = true;
+    usleep(DURATION_SECONDS * 1000 * 1000); // Mimic SynthMark procedure.
+    engine->done = true;
+    return AAUDIO_CALLBACK_RESULT_STOP;
+}
+
+static void s_myErrorCallbackProc(
+    AAudioStream *stream __unused,
+    void *userData __unused,
+    aaudio_result_t error) {
+    printf("%s() - error = %d\n", __func__, error);
+}
+
+static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine) {
+    // Use an AAudioStreamBuilder to contain requested parameters.
+    aaudio_result_t result = AAudio_createStreamBuilder(&engine->builder);
+    if (result != AAUDIO_OK) {
+        printf("AAudio_createStreamBuilder returned %s",
+               AAudio_convertResultToText(result));
+        return result;
+    }
+
+    // Request stream properties.
+    AAudioStreamBuilder_setPerformanceMode(engine->builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+    AAudioStreamBuilder_setDataCallback(engine->builder, s_myDataCallbackProc, engine);
+    AAudioStreamBuilder_setErrorCallback(engine->builder, s_myErrorCallbackProc, engine);
+
+    // Create an AAudioStream using the Builder.
+    result = AAudioStreamBuilder_openStream(engine->builder, &engine->stream);
+    if (result != AAUDIO_OK) {
+        printf("AAudioStreamBuilder_openStream returned %s",
+               AAudio_convertResultToText(result));
+        return result;
+    }
+
+    return result;
+}
+
+int main(int argc, char **argv) {
+    (void) argc;
+    (void) argv;
+    struct AudioEngine engine;
+    aaudio_result_t result = AAUDIO_OK;
+    int errorCount = 0;
+
+    // Make printf print immediately so that debug info is not stuck
+    // in a buffer if we hang or crash.
+    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+    printf("Test Return Stop Hang V1.0\n");
+
+    result = s_OpenAudioStream(&engine);
+    if (result != AAUDIO_OK) {
+        printf("s_OpenAudioStream returned %s\n",
+               AAudio_convertResultToText(result));
+        errorCount++;
+    }
+
+    // Check to see what kind of stream we actually got.
+    int32_t deviceId = AAudioStream_getDeviceId(engine.stream);
+    aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream);
+    printf("-------- opened: deviceId = %3d, perfMode = %d\n", deviceId, actualPerfMode);
+
+    // Start stream.
+    result = AAudioStream_requestStart(engine.stream);
+    printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
+    if (result != AAUDIO_OK) {
+        errorCount++;
+    } else {
+        int counter = 0;
+        while (!engine.started) {
+            printf("Waiting for stream to start, %d\n", counter++);
+            usleep(5 * 1000);
+        }
+        printf("You should see more messages %d seconds after this. If not then the test failed!\n",
+               DURATION_SECONDS);
+        result = engine.join(); // This might hang!
+        AAudioStreamBuilder_delete(engine.builder);
+        engine.builder = nullptr;
+    }
+
+    printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
+    printf("test %s\n", errorCount ? "FAILED" : "PASSED");
+
+    return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
+}