diff --git a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h index de9c60691f..bd12585090 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h @@ -429,7 +429,8 @@ public: /** * Create and open a stream object based on the current settings. * - * The caller owns the pointer to the AudioStream object. + * The caller owns the pointer to the AudioStream object + * and must delete it when finished. * * @deprecated Use openStream(std::shared_ptr &stream) instead. * @param stream pointer to a variable to receive the stream address @@ -455,6 +456,8 @@ public: * The caller must create a unique ptr, and pass by reference so it can be * modified to point to an opened stream. The caller owns the unique ptr, * and it will be automatically closed and deleted when going out of scope. + * + * @deprecated Use openStream(std::shared_ptr &stream) instead. * @param stream Reference to the ManagedStream (uniqueptr) used to keep track of stream * @return OBOE_OK if successful or a negative error code. */ diff --git a/modules/juce_audio_devices/native/oboe/include/oboe/Version.h b/modules/juce_audio_devices/native/oboe/include/oboe/Version.h index fac42d4c9e..1fd1805496 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/Version.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/Version.h @@ -37,7 +37,7 @@ #define OBOE_VERSION_MINOR 5 // Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description. -#define OBOE_VERSION_PATCH 0 +#define OBOE_VERSION_PATCH 1 #define OBOE_STRINGIFY(x) #x #define OBOE_TOSTRING(x) OBOE_STRINGIFY(x) diff --git a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioExtensions.h b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioExtensions.h new file mode 100644 index 0000000000..51a37ae0e3 --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioExtensions.h @@ -0,0 +1,172 @@ +/* + * Copyright 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. + */ + +#ifndef OBOE_AAUDIO_EXTENSIONS_H +#define OBOE_AAUDIO_EXTENSIONS_H + +#include +#include + +#include + +#include "common/OboeDebug.h" +#include "oboe/Oboe.h" +#include "AAudioLoader.h" + +namespace oboe { + +#define LIB_AAUDIO_NAME "libaaudio.so" +#define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed" +#define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy" +#define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy" + +#define AAUDIO_ERROR_UNAVAILABLE static_cast(Result::ErrorUnavailable) + +typedef struct AAudioStreamStruct AAudioStream; + +/** + * Call some AAudio test routines that are not part of the normal API. + */ +class AAudioExtensions { +public: + AAudioExtensions() { + int32_t policy = getIntegerProperty("aaudio.mmap_policy", 0); + mMMapSupported = isPolicyEnabled(policy); + + policy = getIntegerProperty("aaudio.mmap_exclusive_policy", 0); + mMMapExclusiveSupported = isPolicyEnabled(policy); + } + + static bool isPolicyEnabled(int32_t policy) { + return (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS); + } + + static AAudioExtensions &getInstance() { + static AAudioExtensions instance; + return instance; + } + + bool isMMapUsed(oboe::AudioStream *oboeStream) { + AAudioStream *aaudioStream = (AAudioStream *) oboeStream->getUnderlyingStream(); + return isMMapUsed(aaudioStream); + } + + bool isMMapUsed(AAudioStream *aaudioStream) { + if (loadSymbols()) return false; + if (mAAudioStream_isMMap == nullptr) return false; + return mAAudioStream_isMMap(aaudioStream); + } + + /** + * Controls whether the MMAP data path can be selected when opening a stream. + * It has no effect after the stream has been opened. + * It only affects the application that calls it. Other apps are not affected. + * + * @param enabled + * @return 0 or a negative error code + */ + int32_t setMMapEnabled(bool enabled) { + if (loadSymbols()) return AAUDIO_ERROR_UNAVAILABLE; + if (mAAudio_setMMapPolicy == nullptr) return false; + return mAAudio_setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER); + } + + bool isMMapEnabled() { + if (loadSymbols()) return false; + if (mAAudio_getMMapPolicy == nullptr) return false; + int32_t policy = mAAudio_getMMapPolicy(); + return isPolicyEnabled(policy); + } + + bool isMMapSupported() { + return mMMapSupported; + } + + bool isMMapExclusiveSupported() { + return mMMapExclusiveSupported; + } + +private: + + enum { + AAUDIO_POLICY_NEVER = 1, + AAUDIO_POLICY_AUTO, + AAUDIO_POLICY_ALWAYS + }; + typedef int32_t aaudio_policy_t; + + int getIntegerProperty(const char *name, int defaultValue) { + int result = defaultValue; + char valueText[PROP_VALUE_MAX] = {0}; + if (__system_property_get(name, valueText) != 0) { + result = atoi(valueText); + } + return result; + } + + /** + * Load the function pointers. + * This can be called multiple times. + * It should only be called from one thread. + * + * @return 0 if successful or negative error. + */ + aaudio_result_t loadSymbols() { + if (mAAudio_getMMapPolicy != nullptr) { + return 0; + } + + void *libHandle = AAudioLoader::getInstance()->getLibHandle(); + if (libHandle == nullptr) { + LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudioStream_isMMap = (bool (*)(AAudioStream *stream)) + dlsym(libHandle, FUNCTION_IS_MMAP); + if (mAAudioStream_isMMap == nullptr) { + LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy)) + dlsym(libHandle, FUNCTION_SET_MMAP_POLICY); + if (mAAudio_setMMapPolicy == nullptr) { + LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudio_getMMapPolicy = (aaudio_policy_t (*)()) + dlsym(libHandle, FUNCTION_GET_MMAP_POLICY); + if (mAAudio_getMMapPolicy == nullptr) { + LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + return 0; + } + + bool mMMapSupported = false; + bool mMMapExclusiveSupported = false; + + bool (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr; + int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr; + aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr; +}; + +} // namespace oboe + +#endif //OBOE_AAUDIO_EXTENSIONS_H diff --git a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp index 064633c6e5..ec97a26e7d 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp @@ -24,10 +24,17 @@ namespace oboe { AAudioLoader::~AAudioLoader() { - if (mLibHandle != nullptr) { - dlclose(mLibHandle); - mLibHandle = nullptr; - } + // Issue 360: thread_local variables with non-trivial destructors + // will cause segfaults if the containing library is dlclose()ed on + // devices running M or newer, or devices before M when using a static STL. + // The simple workaround is to not call dlclose. + // https://github.com/android/ndk/wiki/Changelog-r22#known-issues + // + // The libaaudio and libaaudioclient do not use thread_local. + // But, to be safe, we should avoid dlclose() if possible. + // Because AAudioLoader is a static Singleton, we can safely skip + // calling dlclose() without causing a resource leak. + LOGI("%s() dlclose(%s) not called, OK", __func__, LIB_AAUDIO_NAME); } AAudioLoader* AAudioLoader::getInstance() { @@ -90,8 +97,6 @@ int AAudioLoader::open() { stream_getTimestamp = load_I_PSKPLPL("AAudioStream_getTimestamp"); - stream_isMMapUsed = load_B_PS("AAudioStream_isMMapUsed"); - stream_getChannelCount = load_I_PS("AAudioStream_getChannelCount"); if (stream_getChannelCount == nullptr) { // Use old alias if needed. @@ -304,7 +309,6 @@ AAudioLoader::signature_I_PSKPLPL AAudioLoader::load_I_PSKPLPL(const char *funct == AAUDIO_PERFORMANCE_MODE_POWER_SAVING, ERRMSG); static_assert((int32_t)PerformanceMode::LowLatency == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, ERRMSG); -#endif // The aaudio_ usage, content and input_preset types were added in NDK 17, // which is the first version to support Android Pie (API 28). @@ -343,6 +347,9 @@ AAudioLoader::signature_I_PSKPLPL AAudioLoader::load_I_PSKPLPL(const char *funct static_assert((int32_t)SessionId::None == AAUDIO_SESSION_ID_NONE, ERRMSG); static_assert((int32_t)SessionId::Allocate == AAUDIO_SESSION_ID_ALLOCATE, ERRMSG); -#endif + +#endif // __NDK_MAJOR__ >= 17 + +#endif // AAUDIO_AAUDIO_H } // namespace oboe diff --git a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h index 6f5bb9502f..a07c53919b 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h @@ -52,6 +52,12 @@ typedef int32_t aaudio_usage_t; typedef int32_t aaudio_content_type_t; typedef int32_t aaudio_input_preset_t; typedef int32_t aaudio_session_id_t; + +// There are a few definitions used by Oboe. +#define AAUDIO_OK static_cast(Result::OK) +#define AAUDIO_ERROR_TIMEOUT static_cast(Result::ErrorTimeout) +#define AAUDIO_STREAM_STATE_STARTING static_cast(StreamState::Starting) +#define AAUDIO_STREAM_STATE_STARTED static_cast(StreamState::Started) #else #include #include @@ -63,7 +69,6 @@ typedef int32_t aaudio_session_id_t; namespace oboe { - /** * The AAudio API was not available in early versions of Android. * To avoid linker errors, we dynamically link with the functions by name using dlsym(). @@ -133,6 +138,8 @@ class AAudioLoader { */ int open(); + void *getLibHandle() const { return mLibHandle; } + // Function pointers into the AAudio shared library. signature_I_PPB createStreamBuilder = nullptr; @@ -167,8 +174,6 @@ class AAudioLoader { signature_I_PSKPLPL stream_getTimestamp = nullptr; - signature_B_PS stream_isMMapUsed = nullptr; - signature_I_PS stream_close = nullptr; signature_I_PS stream_getChannelCount = nullptr; diff --git a/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp b/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp index cb8176233a..bc535aeb7f 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp @@ -23,6 +23,7 @@ #include "common/AudioClock.h" #include "common/OboeDebug.h" #include "oboe/Utilities.h" +#include "AAudioExtensions.h" #ifdef __ANDROID__ #include @@ -677,7 +678,7 @@ ResultWithValue AudioStreamAAudio::calculateLatencyMillis() { bool AudioStreamAAudio::isMMapUsed() { AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { - return mLibLoader->stream_isMMapUsed(stream); + return AAudioExtensions::getInstance().isMMapUsed(stream); } else { return false; } diff --git a/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp b/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp index dffcd75e8c..b9f04b8512 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp @@ -16,6 +16,8 @@ #include + +#include "aaudio/AAudioExtensions.h" #include "aaudio/AudioStreamAAudio.h" #include "FilterAudioStream.h" #include "OboeDebug.h" @@ -109,7 +111,6 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) { // Do we need to make a child stream and convert. if (conversionNeeded) { AudioStream *tempStream; - result = childBuilder.openStream(&tempStream); if (result != Result::OK) { return result; @@ -156,7 +157,20 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) { } } - result = streamP->open(); // TODO review API + // If MMAP has a problem in this case then disable it temporarily. + bool wasMMapOriginallyEnabled = AAudioExtensions::getInstance().isMMapEnabled(); + bool wasMMapTemporarilyDisabled = false; + if (wasMMapOriginallyEnabled) { + bool isMMapSafe = QuirksManager::getInstance().isMMapSafe(childBuilder); + if (!isMMapSafe) { + AAudioExtensions::getInstance().setMMapEnabled(false); + wasMMapTemporarilyDisabled = true; + } + } + result = streamP->open(); + if (wasMMapTemporarilyDisabled) { + AAudioExtensions::getInstance().setMMapEnabled(wasMMapOriginallyEnabled); // restore original + } if (result == Result::OK) { int32_t optimalBufferSize = -1; diff --git a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp index aa285de07a..5d35c71b3e 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp @@ -20,10 +20,6 @@ #include "OboeDebug.h" #include "QuirksManager.h" -#ifndef __ANDROID_API_R__ -#define __ANDROID_API_R__ 30 -#endif - using namespace oboe; int32_t QuirksManager::DeviceQuirks::clipBufferSize(AudioStream &stream, @@ -74,6 +70,9 @@ public: std::string chipname = getPropertyString("ro.hardware.chipname"); isExynos9810 = (chipname == "exynos9810"); + isExynos990 = (chipname == "exynos990"); + + mBuildChangelist = getPropertyInteger("ro.build.changelist", 0); } virtual ~SamsungDeviceQuirks() = default; @@ -98,6 +97,17 @@ public: && builder.getInputPreset() != oboe::InputPreset::Camcorder; } + bool isMMapSafe(const AudioStreamBuilder &builder) override { + const bool isInput = builder.getDirection() == Direction::Input; + // This detects b/159066712 , S20 LSI has corrupt low latency audio recording + // and turns off MMAP. + // See also https://github.com/google/oboe/issues/892 + bool mRecordingCorrupted = isInput + && isExynos990 + && mBuildChangelist < 19350896; + return !mRecordingCorrupted; + } + private: // Stay farther away from DSP position on Exynos devices. static constexpr int32_t kBottomMarginExynos = 2; @@ -105,6 +115,8 @@ private: static constexpr int32_t kTopMargin = 1; bool isExynos = false; bool isExynos9810 = false; + bool isExynos990 = false; + int mBuildChangelist = 0; }; QuirksManager::QuirksManager() { @@ -203,3 +215,8 @@ bool QuirksManager::isConversionNeeded( return conversionNeeded; } + +bool QuirksManager::isMMapSafe(AudioStreamBuilder &builder) { + if (!OboeGlobals::areWorkaroundsEnabled()) return true; + return mDeviceQuirks->isMMapSafe(builder); +} diff --git a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h index b4e38ded7e..aeca270ac9 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h +++ b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h @@ -21,6 +21,10 @@ #include #include +#ifndef __ANDROID_API_R__ +#define __ANDROID_API_R__ 30 +#endif + namespace oboe { /** @@ -98,6 +102,10 @@ public: virtual bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const; + virtual bool isMMapSafe(const AudioStreamBuilder & /* builder */ ) { + return true; + } + static constexpr int32_t kDefaultBottomMarginInBursts = 0; static constexpr int32_t kDefaultTopMarginInBursts = 0; @@ -108,6 +116,8 @@ public: static constexpr int32_t kCommonNativeRate = 48000; // very typical native sample rate }; + bool isMMapSafe(AudioStreamBuilder &builder); + private: static constexpr int32_t kChannelCountMono = 1;