You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

304 lines
11 KiB

/*
* 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 <dlfcn.h>
#include <set>
#include <stdint.h>
#include <sys/system_properties.h>
#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<aaudio_result_t>(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<DeviceType> 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<DeviceType> 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<MMapPolicy>(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<aaudio_policy_t>(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<int32_t>(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<MMapPolicy>(mLibLoader->aaudio_getMMapPolicy());
return policy == MMapPolicy::Unspecified
? mMMapSupported : isPolicyEnabled(static_cast<int32_t>(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<MMapPolicy>(mLibLoader->aaudio_getPlatformMMapPolicy(
static_cast<AAudio_DeviceType>(deviceType),
static_cast<aaudio_direction_t>(direction)));
}
MMapPolicy getMMapExclusivePolicy(DeviceType deviceType, Direction direction) {
if (mLibLoader == nullptr ||
mLibLoader->aaudio_getPlatformMMapExclusivePolicy == nullptr) {
return MMapPolicy::Unspecified;
}
return static_cast<MMapPolicy>(mLibLoader->aaudio_getPlatformMMapExclusivePolicy(
static_cast<AAudio_DeviceType>(deviceType),
static_cast<aaudio_direction_t>(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<int32_t>(
getMMapPolicy(deviceType, Direction::Input)));
}) ||
std::any_of(ALL_OUTPUT_DEVICE_TYPES.begin(), ALL_OUTPUT_DEVICE_TYPES.end(),
[this](DeviceType deviceType) {
return isPolicyEnabled(static_cast<int32_t>(
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<int32_t>(
getMMapExclusivePolicy(deviceType, Direction::Input)));
}) ||
std::any_of(ALL_OUTPUT_DEVICE_TYPES.begin(), ALL_OUTPUT_DEVICE_TYPES.end(),
[this](DeviceType deviceType) {
return isPolicyEnabled(static_cast<int32_t>(
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