aaudio examples: handle disconnect in write_sine_callback

Automatically reopen the stream if we get disconnected.
Add a futex based signaling class so that the error callback can
immediately inform a waiting thread.

This can be used to prevent the system from playing an unpleasant sound
after the stream is disconnected.

Bug: 63342351
Test: plug and unplug headphones while running write_sine_callback
Change-Id: I3ea4fb24106156a29d2f302f3eb614f25f1b758a
diff --git a/media/libaaudio/examples/utils/AAudioExampleUtils.h b/media/libaaudio/examples/utils/AAudioExampleUtils.h
index 6cbcc58..9ef62c9 100644
--- a/media/libaaudio/examples/utils/AAudioExampleUtils.h
+++ b/media/libaaudio/examples/utils/AAudioExampleUtils.h
@@ -17,9 +17,14 @@
 #ifndef AAUDIO_EXAMPLE_UTILS_H
 #define AAUDIO_EXAMPLE_UTILS_H
 
-#include <unistd.h>
+#include <atomic>
+#include <linux/futex.h>
 #include <sched.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
 #include <aaudio/AAudio.h>
+#include <utils/Errors.h>
 
 #define NANOS_PER_MICROSECOND ((int64_t)1000)
 #define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
@@ -40,6 +45,12 @@
     return modeText;
 }
 
+static void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
+    time->tv_sec = nanoseconds / NANOS_PER_SECOND;
+    // Calculate the fractional nanoseconds. Avoids expensive % operation.
+    time->tv_nsec = nanoseconds - (time->tv_sec * NANOS_PER_SECOND);
+}
+
 static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
     struct timespec time;
     int result = clock_gettime(clockId, &time);
@@ -79,4 +90,77 @@
     return latencyMillis;
 }
 
+// ================================================================================
+// These Futex calls are common online examples.
+static android::status_t sys_futex(void *addr1, int op, int val1,
+                      struct timespec *timeout, void *addr2, int val3) {
+    android::status_t result = (android::status_t) syscall(SYS_futex, addr1,
+                                                           op, val1, timeout,
+                                                           addr2, val3);
+    return (result == 0) ? 0 : -errno;
+}
+
+static android::status_t futex_wake(void *addr, int numWake) {
+    // Use _PRIVATE because we are just using the futex in one process.
+    return sys_futex(addr, FUTEX_WAKE_PRIVATE, numWake, NULL, NULL, 0);
+}
+
+static android::status_t futex_wait(void *addr, int current, struct timespec *time) {
+    // Use _PRIVATE because we are just using the futex in one process.
+    return sys_futex(addr, FUTEX_WAIT_PRIVATE, current, time, NULL, 0);
+}
+
+// TODO better name?
+/**
+ * The WakeUp class is used to send a wakeup signal to one or more sleeping threads.
+ */
+class WakeUp {
+public:
+    WakeUp() : mValue(0) {}
+    explicit WakeUp(int32_t value) : mValue(value) {}
+
+    /**
+     * Wait until the internal value no longer matches the given value.
+     * Note that this code uses a futex, which is subject to spurious wake-ups.
+     * So check to make sure that the desired condition has been met.
+     *
+     * @return zero if the value changes or various negative errors including
+     *    -ETIMEDOUT if a timeout occurs,
+     *    or -EINTR if interrupted by a signal,
+     *    or -EAGAIN or -EWOULDBLOCK if the internal value does not match the specified value
+     */
+    android::status_t wait(int32_t value, int64_t timeoutNanoseconds) {
+        struct timespec time;
+        convertNanosecondsToTimespec(timeoutNanoseconds, &time);
+        return futex_wait(&mValue, value, &time);
+    }
+
+    /**
+     * Increment value and wake up any threads that need to be woken.
+     *
+     * @return number of waiters woken up
+     */
+    android::status_t wake() {
+        ++mValue;
+        return futex_wake(&mValue, INT_MAX);
+    }
+
+    /**
+     * Set value and wake up any threads that need to be woken.
+     *
+     * @return number of waiters woken up
+     */
+    android::status_t wake(int32_t value) {
+        mValue.store(value);
+        return futex_wake(&mValue, INT_MAX);
+    }
+
+    int32_t get() {
+        return mValue.load();
+    }
+
+private:
+    std::atomic<int32_t>   mValue;
+};
+
 #endif // AAUDIO_EXAMPLE_UTILS_H