Merge pull request #70 from google/getstream

oboe: add debug method for getting AAudioStream
diff --git a/include/oboe/AudioStream.h b/include/oboe/AudioStream.h
index 57e48b2..8317ad4 100644
--- a/include/oboe/AudioStream.h
+++ b/include/oboe/AudioStream.h
@@ -186,12 +186,18 @@
         return static_cast<int32_t>(Result::ErrorUnimplemented);
     }
 
+
     /**
      *
+     * @return the API that this stream uses
+     */
+    virtual AudioApi getAudioApi() const = 0;
+
+    /**
      * @return true if this stream is implemented using the AAudio API
      */
-    virtual bool usesAAudio() const {
-        return false;
+    bool usesAAudio() const {
+        return getAudioApi() == AudioApi::AAudio;
     }
 
     /**
diff --git a/include/oboe/AudioStreamBuilder.h b/include/oboe/AudioStreamBuilder.h
index 34c38b8..462e745 100644
--- a/include/oboe/AudioStreamBuilder.h
+++ b/include/oboe/AudioStreamBuilder.h
@@ -81,7 +81,7 @@
      * oriented operations, then call this function to get the sizes you need.
      *
      * @param framesPerCallback
-     * @return
+     * @return pointer to the builder so calls can be chained
      */
     AudioStreamBuilder *setFramesPerCallback(int framesPerCallback) {
         mFramesPerCallback = framesPerCallback;
@@ -105,7 +105,7 @@
      *
      * Default is kUnspecified.
      *
-     * @param frames the desired buffer capacity in frames or kUnspecified
+     * @param bufferCapacityInFrames the desired buffer capacity in frames or kUnspecified
      * @return pointer to the builder so calls can be chained
      */
     AudioStreamBuilder *setBufferCapacityInFrames(int32_t bufferCapacityInFrames) {
@@ -116,9 +116,12 @@
     AudioApi getAudioApi() const { return mAudioApi; }
 
     /**
-     * Normally you would leave this unspecified, and Oboe will chose the best API
-     * for the device at runtime.
-     * @param Must be AudioApi::Unspecified, AudioApi::OpenSLES or AudioApi::AAudio.
+     * If you leave this unspecified then Oboe will choose the best API
+     * for the device and SDK version at runtime.
+     *
+     * If the caller requests AAudio and it is supported then AAudio will be used.
+     *
+     * @param audioApi Must be AudioApi::Unspecified, AudioApi::OpenSLES or AudioApi::AAudio.
      * @return pointer to the builder so calls can be chained
      */
     AudioStreamBuilder *setAudioApi(AudioApi audioApi) {
@@ -136,6 +139,15 @@
     static bool isAAudioSupported();
 
     /**
+     * Is the AAudio API recommended this device?
+     *
+     * AAudio may be supported but not recommended because of version specific issues.
+     *
+     * @return true if recommended
+     */
+    static bool isAAudioRecommended();
+
+    /**
      * Request a mode for sharing the device.
      * The requested sharing mode may not be available.
      * So the application should query for the actual mode after the stream is opened.
diff --git a/include/oboe/Utilities.h b/include/oboe/Utilities.h
index 4f2b139..29a9b6d 100644
--- a/include/oboe/Utilities.h
+++ b/include/oboe/Utilities.h
@@ -43,6 +43,15 @@
 template <typename FromType>
 const char * convertToText(FromType);
 
+/**
+ * Return the version of the SDK that is currently running.
+ *
+ * For example, on Android, this would return 27 for Oreo 8.1.
+ * If the version number cannot be determined then this will return -1.
+ *
+ * @return version number or -1
+ */
+int getSdkVersion();
 
 } // namespace oboe
 
diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp
index d44ee9c..0b47fcd 100644
--- a/src/aaudio/AudioStreamAAudio.cpp
+++ b/src/aaudio/AudioStreamAAudio.cpp
@@ -83,34 +83,17 @@
     isSupported();
 }
 
-AudioStreamAAudio::~AudioStreamAAudio()
-{
+AudioStreamAAudio::~AudioStreamAAudio() {
     delete[] mFloatCallbackBuffer;
     delete[] mShortCallbackBuffer;
 }
 
-static bool isRunningOnAndroid8_1OrHigher() {
-#ifdef __ANDROID__
-  char sdk[PROP_VALUE_MAX] = {0};
-  if (__system_property_get("ro.build.version.sdk", sdk) != 0) {
-      return atoi(sdk) >= 27;  // SDK Level 27 is Android Oreo 8.1
-  }
-#endif
-  return false;
-}
-
 bool AudioStreamAAudio::isSupported() {
-    if (!isRunningOnAndroid8_1OrHigher()) {
-        // See https://github.com/google/oboe/issues/40,
-        // AAudio is not stable enough on Android 8.0.
-        return false;
-    }
     mLibLoader = AAudioLoader::getInstance();
     int openResult = mLibLoader->open();
     return openResult == 0;
 }
 
-
 Result AudioStreamAAudio::open() {
     Result result = Result::OK;
 
diff --git a/src/aaudio/AudioStreamAAudio.h b/src/aaudio/AudioStreamAAudio.h
index 73f1286..caf53c1 100644
--- a/src/aaudio/AudioStreamAAudio.h
+++ b/src/aaudio/AudioStreamAAudio.h
@@ -87,8 +87,8 @@
     StreamState getState() override;
 
 
-    bool usesAAudio() const override {
-        return true;
+    AudioApi getAudioApi() const override {
+        return AudioApi::AAudio;
     }
 
     DataCallbackResult callOnAudioReady(AAudioStream *stream,
diff --git a/src/common/AudioStreamBuilder.cpp b/src/common/AudioStreamBuilder.cpp
index a3582e8..777b573 100644
--- a/src/common/AudioStreamBuilder.cpp
+++ b/src/common/AudioStreamBuilder.cpp
@@ -30,25 +30,27 @@
     return AudioStreamAAudio::isSupported();
 }
 
+bool AudioStreamBuilder::isAAudioRecommended() {
+    const int ANDROID_8_1 = 27; // OC-MR1
+    // See https://github.com/google/oboe/issues/40,
+    // AAudio may not be stable on Android 8.0, depending on how it is used.
+    return (getSdkVersion() >= ANDROID_8_1);
+}
+
 AudioStream *AudioStreamBuilder::build() {
-    LOGD("AudioStreamBuilder.build(): mAudioApi %d, mChannelCount = %d, mFramesPerCallback = %d",
-         mAudioApi, mChannelCount, mFramesPerCallback);
     AudioStream *stream = nullptr;
-    switch(mAudioApi) {
-        case AudioApi::Unspecified:
-        case AudioApi::AAudio:
-            if (AudioStreamAAudio::isSupported()) {
-                stream = new AudioStreamAAudio(*this);
-                break;
-            }
-            // fall into using older existing API
-        case AudioApi::OpenSLES:
-            if (getDirection() == oboe::Direction::Output) {
-                stream = new AudioOutputStreamOpenSLES(*this);
-            } else if (getDirection() == oboe::Direction::Input) {
-                stream = new AudioInputStreamOpenSLES(*this);
-            }
-            break;
+    if (mAudioApi == AudioApi::AAudio && isAAudioSupported()) {
+        stream = new AudioStreamAAudio(*this);
+
+    // If unspecified, only use AAudio if supported and recommended.
+    } else if (mAudioApi == AudioApi::Unspecified && isAAudioSupported() && isAAudioRecommended()) {
+        stream = new AudioStreamAAudio(*this);
+    } else {
+        if (getDirection() == oboe::Direction::Output) {
+            stream = new AudioOutputStreamOpenSLES(*this);
+        } else if (getDirection() == oboe::Direction::Input) {
+            stream = new AudioInputStreamOpenSLES(*this);
+        }
     }
     return stream;
 }
diff --git a/src/common/Utilities.cpp b/src/common/Utilities.cpp
index 04dc426..42c86b0 100644
--- a/src/common/Utilities.cpp
+++ b/src/common/Utilities.cpp
@@ -15,7 +15,13 @@
  */
 
 
+#include <stdlib.h>
 #include <unistd.h>
+
+#ifdef __ANDROID__
+#include <sys/system_properties.h>
+#endif
+
 #include "oboe/Definitions.h"
 #include "oboe/Utilities.h"
 
@@ -162,4 +168,14 @@
     }
 }
 
-}// namespace oboe
\ No newline at end of file
+int getSdkVersion() {
+#ifdef __ANDROID__
+    char sdk[PROP_VALUE_MAX] = {0};
+    if (__system_property_get("ro.build.version.sdk", sdk) != 0) {
+        return atoi(sdk);
+    }
+#endif
+    return -1;
+}
+
+}// namespace oboe
diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h
index a524387..22fd223 100644
--- a/src/opensles/AudioStreamOpenSLES.h
+++ b/src/opensles/AudioStreamOpenSLES.h
@@ -58,6 +58,11 @@
 
     int32_t getFramesPerBurst() override;
 
+
+    AudioApi getAudioApi() const override {
+        return AudioApi::OpenSLES;
+    }
+
     /**
      * Process next OpenSL ES buffer.
      * Called by by OpenSL ES framework.