/* * 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 #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; // The output device type collection must be updated if there is any new added output device type const static std::set ALL_OUTPUT_DEVICE_TYPES = { DeviceType::BuiltinEarpiece, DeviceType::BuiltinSpeaker, DeviceType::WiredHeadset, DeviceType::WiredHeadphones, DeviceType::LineAnalog, DeviceType::LineDigital, DeviceType::BluetoothSco, DeviceType::BluetoothA2dp, DeviceType::Hdmi, DeviceType::HdmiArc, DeviceType::HdmiEarc, DeviceType::UsbDevice, DeviceType::UsbHeadset, DeviceType::UsbAccessory, DeviceType::Dock, DeviceType::DockAnalog, DeviceType::FM, DeviceType::Telephony, DeviceType::AuxLine, DeviceType::IP, DeviceType::Bus, DeviceType::HearingAid, DeviceType::BuiltinSpeakerSafe, DeviceType::RemoteSubmix, DeviceType::BleHeadset, DeviceType::BleSpeaker, DeviceType::BleBroadcast, }; // The input device type collection must be updated if there is any new added input device type const static std::set ALL_INPUT_DEVICE_TYPES = { DeviceType::BuiltinMic, DeviceType::BluetoothSco, DeviceType::WiredHeadset, DeviceType::Hdmi, DeviceType::Telephony, DeviceType::Dock, DeviceType::DockAnalog, DeviceType::UsbAccessory, DeviceType::UsbDevice, DeviceType::UsbHeadset, DeviceType::FMTuner, DeviceType::TVTuner, DeviceType::LineAnalog, DeviceType::LineDigital, DeviceType::BluetoothA2dp, DeviceType::IP, DeviceType::Bus, DeviceType::RemoteSubmix, DeviceType::BleHeadset, DeviceType::HdmiArc, DeviceType::HdmiEarc, }; /** * Call some AAudio test routines that are not part of the normal API. */ class AAudioExtensions { private: // Because it is a singleton. Call getInstance() instead. AAudioExtensions() { mLibLoader = AAudioLoader::getInstance(); if (!initMMapPolicy()) { int32_t policy = getIntegerProperty("aaudio.mmap_policy", 0); mMMapSupported = isPolicyEnabled(policy); policy = getIntegerProperty("aaudio.mmap_exclusive_policy", 0); mMMapExclusiveSupported = isPolicyEnabled(policy); } } public: static bool isPolicyEnabled(int32_t policy) { const MMapPolicy mmapPolicy = static_cast(policy); return (mmapPolicy == MMapPolicy::Auto || mmapPolicy == MMapPolicy::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 (mLibLoader != nullptr && mLibLoader->stream_isMMapUsed != nullptr) { return mLibLoader->stream_isMMapUsed(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) { // The API for setting mmap policy is public after API level 36. if (mLibLoader != nullptr && mLibLoader->aaudio_setMMapPolicy != nullptr) { return mLibLoader->aaudio_setMMapPolicy( static_cast(enabled ? MMapPolicy::Auto : MMapPolicy::Never)); } // When there is no public API, fallback to loading the symbol from hidden API. if (loadSymbols()) return AAUDIO_ERROR_UNAVAILABLE; if (mAAudio_setMMapPolicy == nullptr) return false; return mAAudio_setMMapPolicy( static_cast(enabled ? MMapPolicy::Auto : MMapPolicy::Never)); } bool isMMapEnabled() { // The API for getting mmap policy is public after API level 36. // Use it when it is available. if (mLibLoader != nullptr && mLibLoader->aaudio_getMMapPolicy != nullptr) { MMapPolicy policy = static_cast(mLibLoader->aaudio_getMMapPolicy()); return policy == MMapPolicy::Unspecified ? mMMapSupported : isPolicyEnabled(static_cast(policy)); } // When there is no public API, fallback to loading the symbol from hidden API. if (loadSymbols()) return false; if (mAAudio_getMMapPolicy == nullptr) return false; int32_t policy = mAAudio_getMMapPolicy(); return (policy == Unspecified) ? mMMapSupported : isPolicyEnabled(policy); } bool isMMapSupported() { return mMMapSupported; } bool isMMapExclusiveSupported() { return mMMapExclusiveSupported; } MMapPolicy getMMapPolicy(DeviceType deviceType, Direction direction) { if (mLibLoader == nullptr || mLibLoader->aaudio_getPlatformMMapPolicy == nullptr) { return MMapPolicy::Unspecified; } return static_cast(mLibLoader->aaudio_getPlatformMMapPolicy( static_cast(deviceType), static_cast(direction))); } MMapPolicy getMMapExclusivePolicy(DeviceType deviceType, Direction direction) { if (mLibLoader == nullptr || mLibLoader->aaudio_getPlatformMMapExclusivePolicy == nullptr) { return MMapPolicy::Unspecified; } return static_cast(mLibLoader->aaudio_getPlatformMMapExclusivePolicy( static_cast(deviceType), static_cast(direction))); } private: bool initMMapPolicy() { if (mLibLoader == nullptr || mLibLoader->open() != 0) { return false; } if (mLibLoader->aaudio_getPlatformMMapPolicy == nullptr || mLibLoader->aaudio_getPlatformMMapExclusivePolicy == nullptr) { return false; } mMMapSupported = std::any_of(ALL_INPUT_DEVICE_TYPES.begin(), ALL_INPUT_DEVICE_TYPES.end(), [this](DeviceType deviceType) { return isPolicyEnabled(static_cast( getMMapPolicy(deviceType, Direction::Input))); }) || std::any_of(ALL_OUTPUT_DEVICE_TYPES.begin(), ALL_OUTPUT_DEVICE_TYPES.end(), [this](DeviceType deviceType) { return isPolicyEnabled(static_cast( getMMapPolicy(deviceType, Direction::Output))); }); mMMapExclusiveSupported = std::any_of(ALL_INPUT_DEVICE_TYPES.begin(), ALL_INPUT_DEVICE_TYPES.end(), [this](DeviceType deviceType) { return isPolicyEnabled(static_cast( getMMapExclusivePolicy(deviceType, Direction::Input))); }) || std::any_of(ALL_OUTPUT_DEVICE_TYPES.begin(), ALL_OUTPUT_DEVICE_TYPES.end(), [this](DeviceType deviceType) { return isPolicyEnabled(static_cast( getMMapExclusivePolicy(deviceType, Direction::Output))); }); return true; } 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; } if (mLibLoader == nullptr || mLibLoader->open() != 0) { LOGD("%s() could not open " LIB_AAUDIO_NAME, __func__); return AAUDIO_ERROR_UNAVAILABLE; } void *libHandle = mLibLoader->getLibHandle(); if (libHandle == nullptr) { LOGE("%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; AAudioLoader *mLibLoader; }; } // namespace oboe #endif //OBOE_AAUDIO_EXTENSIONS_H