aaudio: fix output bugs and improve input performance, add loopback test

Support AAUDIO_PERFORMANCE_MODE in AudioStreamRecord.cpp
Fix race condition when closing a stream, which this test revealed.
Fix setting of negative notificationFrames for non-FAST tracks.

Convert test from old Oboe API to AAudio.
Add command line options to the test.
Add systrace calls.

Bug: 34093052
Bug: 38313432
Bug: 38178592
Test: loopback.cpp
Change-Id: Ib6d2995cdd3ed432937fde2f26c5394013f0d6e0
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index 027d66d..e6751c49 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -182,6 +182,15 @@
     mDownDataQueue->getEmptyRoomAvailable(wrappingBuffer);
 }
 
+int32_t AudioEndpoint::getEmptyFramesAvailable() {
+    return mDownDataQueue->getFifoControllerBase()->getEmptyFramesAvailable();
+}
+
+int32_t AudioEndpoint::getFullFramesAvailable()
+{
+    return mDownDataQueue->getFifoControllerBase()->getFullFramesAvailable();
+}
+
 void AudioEndpoint::advanceWriteIndex(int32_t deltaFrames) {
     mDownDataQueue->getFifoControllerBase()->advanceWriteIndex(deltaFrames);
 }
@@ -227,7 +236,3 @@
     return (int32_t)mDownDataQueue->getBufferCapacityInFrames();
 }
 
-int32_t AudioEndpoint::getFullFramesAvailable()
-{
-    return mDownDataQueue->getFifoControllerBase()->getFullFramesAvailable();
-}
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index 46a3fc5..3a2099f 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -56,6 +56,9 @@
 
     void getEmptyRoomAvailable(android::WrappingBuffer *wrappingBuffer);
 
+    int32_t getEmptyFramesAvailable();
+    int32_t getFullFramesAvailable();
+
     void advanceWriteIndex(int32_t deltaFrames);
 
     /**
@@ -81,8 +84,6 @@
 
     int32_t getBufferCapacityInFrames() const;
 
-    int32_t getFullFramesAvailable();
-
 private:
     android::FifoBuffer    *mUpCommandQueue;
     android::FifoBuffer    *mDownDataQueue;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index eee860e..143d4b7 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -18,6 +18,8 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
 #include <stdint.h>
 #include <assert.h>
 
@@ -25,6 +27,7 @@
 
 #include <aaudio/AAudio.h>
 #include <utils/String16.h>
+#include <utils/Trace.h>
 
 #include "AudioClock.h"
 #include "AudioEndpointParcelable.h"
@@ -188,11 +191,25 @@
     ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal.close(): mServiceStreamHandle = 0x%08X",
              mServiceStreamHandle);
     if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
+        // Don't close a stream while it is running.
+        aaudio_stream_state_t currentState = getState();
+        if (isPlaying()) {
+            requestStop();
+            aaudio_stream_state_t nextState;
+            int64_t timeoutNanoseconds = MIN_TIMEOUT_NANOS;
+            aaudio_result_t result = waitForStateChange(currentState, &nextState,
+                                                       timeoutNanoseconds);
+            if (result != AAUDIO_OK) {
+                ALOGE("AudioStreamInternal::close() waitForStateChange() returned %d %s",
+                result, AAudio_convertResultToText(result));
+            }
+        }
         aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
         mServiceStreamHandle = AAUDIO_HANDLE_INVALID;
 
         mServiceInterface.closeStream(serviceStreamHandle);
         delete[] mCallbackBuffer;
+        mCallbackBuffer = nullptr;
         return mEndPointParcelable.close();
     } else {
         return AAUDIO_ERROR_INVALID_HANDLE;
@@ -524,6 +541,8 @@
 aaudio_result_t AudioStreamInternal::write(const void *buffer, int32_t numFrames,
                                          int64_t timeoutNanoseconds)
 {
+    const char * traceName = (mInService) ? "aaWrtS" : "aaWrtC";
+    ATRACE_BEGIN(traceName);
     aaudio_result_t result = AAUDIO_OK;
     int32_t loopCount = 0;
     uint8_t* source = (uint8_t*)buffer;
@@ -531,6 +550,12 @@
     int64_t deadlineNanos = currentTimeNanos + timeoutNanoseconds;
     int32_t framesLeft = numFrames;
 
+    int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+    if (ATRACE_ENABLED()) {
+        const char * traceName = (mInService) ? "aaFullS" : "aaFullC";
+        ATRACE_INT(traceName, fullFrames);
+    }
+
     // Write until all the data has been written or until a timeout occurs.
     while (framesLeft > 0) {
         // The call to writeNow() will not block. It will just write as much as it can.
@@ -568,6 +593,7 @@
 
     // return error or framesWritten
     (void) loopCount;
+    ATRACE_END();
     return (result < 0) ? result : numFrames - framesLeft;
 }