477 lines
18 KiB
C++
477 lines
18 KiB
C++
/*
|
|
* Copyright (C) 2015 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.
|
|
*/
|
|
|
|
#define LOG_TAG "APM::HwModule"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <android-base/stringprintf.h>
|
|
#include <policy.h>
|
|
#include <system/audio.h>
|
|
|
|
#include "HwModule.h"
|
|
#include "IOProfile.h"
|
|
|
|
namespace android {
|
|
|
|
HwModule::HwModule(const char *name, uint32_t halVersionMajor, uint32_t halVersionMinor)
|
|
: mName(String8(name)),
|
|
mHandle(AUDIO_MODULE_HANDLE_NONE)
|
|
{
|
|
setHalVersion(halVersionMajor, halVersionMinor);
|
|
}
|
|
|
|
HwModule::~HwModule()
|
|
{
|
|
for (size_t i = 0; i < mOutputProfiles.size(); i++) {
|
|
mOutputProfiles[i]->clearSupportedDevices();
|
|
}
|
|
for (size_t i = 0; i < mInputProfiles.size(); i++) {
|
|
mInputProfiles[i]->clearSupportedDevices();
|
|
}
|
|
}
|
|
|
|
std::string HwModule::getTagForDevice(audio_devices_t device, const String8 &address,
|
|
audio_format_t codec)
|
|
{
|
|
DeviceVector declaredDevices = getDeclaredDevices();
|
|
sp<DeviceDescriptor> deviceDesc = declaredDevices.getDevice(device, address, codec);
|
|
return deviceDesc ? deviceDesc->getTagName() : std::string{};
|
|
}
|
|
|
|
status_t HwModule::addOutputProfile(const std::string& name, const audio_config_t *config,
|
|
audio_devices_t device, const String8& address)
|
|
{
|
|
sp<IOProfile> profile = new OutputProfile(name);
|
|
|
|
profile->addAudioProfile(new AudioProfile(config->format, config->channel_mask,
|
|
config->sample_rate));
|
|
|
|
sp<DeviceDescriptor> devDesc =
|
|
new DeviceDescriptor(device, getTagForDevice(device), address.string());
|
|
addDynamicDevice(devDesc);
|
|
// Reciprocally attach the device to the module
|
|
devDesc->attach(this);
|
|
profile->addSupportedDevice(devDesc);
|
|
|
|
return addOutputProfile(profile);
|
|
}
|
|
|
|
status_t HwModule::addOutputProfile(const sp<IOProfile> &profile)
|
|
{
|
|
profile->attach(this);
|
|
mOutputProfiles.add(profile);
|
|
mPorts.add(profile);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t HwModule::addInputProfile(const sp<IOProfile> &profile)
|
|
{
|
|
profile->attach(this);
|
|
mInputProfiles.add(profile);
|
|
mPorts.add(profile);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t HwModule::addProfile(const sp<IOProfile> &profile)
|
|
{
|
|
switch (profile->getRole()) {
|
|
case AUDIO_PORT_ROLE_SOURCE:
|
|
return addOutputProfile(profile);
|
|
case AUDIO_PORT_ROLE_SINK:
|
|
return addInputProfile(profile);
|
|
case AUDIO_PORT_ROLE_NONE:
|
|
return BAD_VALUE;
|
|
}
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
void HwModule::setProfiles(const IOProfileCollection &profiles)
|
|
{
|
|
for (size_t i = 0; i < profiles.size(); i++) {
|
|
addProfile(profiles[i]);
|
|
}
|
|
}
|
|
|
|
status_t HwModule::removeOutputProfile(const std::string& name)
|
|
{
|
|
for (size_t i = 0; i < mOutputProfiles.size(); i++) {
|
|
if (mOutputProfiles[i]->getName() == name) {
|
|
for (const auto &device : mOutputProfiles[i]->getSupportedDevices()) {
|
|
removeDynamicDevice(device);
|
|
}
|
|
mOutputProfiles.removeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t HwModule::addInputProfile(const std::string& name, const audio_config_t *config,
|
|
audio_devices_t device, const String8& address)
|
|
{
|
|
sp<IOProfile> profile = new InputProfile(name);
|
|
profile->addAudioProfile(new AudioProfile(config->format, config->channel_mask,
|
|
config->sample_rate));
|
|
|
|
sp<DeviceDescriptor> devDesc =
|
|
new DeviceDescriptor(device, getTagForDevice(device), address.string());
|
|
addDynamicDevice(devDesc);
|
|
// Reciprocally attach the device to the module
|
|
devDesc->attach(this);
|
|
profile->addSupportedDevice(devDesc);
|
|
|
|
ALOGV("addInputProfile() name %s rate %d mask 0x%08x",
|
|
name.c_str(), config->sample_rate, config->channel_mask);
|
|
|
|
return addInputProfile(profile);
|
|
}
|
|
|
|
status_t HwModule::removeInputProfile(const std::string& name)
|
|
{
|
|
for (size_t i = 0; i < mInputProfiles.size(); i++) {
|
|
if (mInputProfiles[i]->getName() == name) {
|
|
for (const auto &device : mInputProfiles[i]->getSupportedDevices()) {
|
|
removeDynamicDevice(device);
|
|
}
|
|
mInputProfiles.removeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void HwModule::setDeclaredDevices(const DeviceVector &devices)
|
|
{
|
|
mDeclaredDevices = devices;
|
|
for (size_t i = 0; i < devices.size(); i++) {
|
|
mPorts.add(devices[i]);
|
|
}
|
|
}
|
|
|
|
sp<DeviceDescriptor> HwModule::getRouteSinkDevice(const sp<AudioRoute> &route) const
|
|
{
|
|
sp<DeviceDescriptor> sinkDevice = 0;
|
|
if (route->getSink()->asAudioPort()->getType() == AUDIO_PORT_TYPE_DEVICE) {
|
|
sinkDevice = mDeclaredDevices.getDeviceFromTagName(route->getSink()->getTagName());
|
|
}
|
|
return sinkDevice;
|
|
}
|
|
|
|
DeviceVector HwModule::getRouteSourceDevices(const sp<AudioRoute> &route) const
|
|
{
|
|
DeviceVector sourceDevices;
|
|
for (const auto& source : route->getSources()) {
|
|
if (source->asAudioPort()->getType() == AUDIO_PORT_TYPE_DEVICE) {
|
|
sourceDevices.add(mDeclaredDevices.getDeviceFromTagName(source->getTagName()));
|
|
}
|
|
}
|
|
return sourceDevices;
|
|
}
|
|
|
|
void HwModule::setRoutes(const AudioRouteVector &routes)
|
|
{
|
|
mRoutes = routes;
|
|
// Now updating the streams (aka IOProfile until now) supported devices
|
|
refreshSupportedDevices();
|
|
}
|
|
|
|
void HwModule::refreshSupportedDevices()
|
|
{
|
|
// Now updating the streams (aka IOProfile until now) supported devices
|
|
for (const auto& stream : mInputProfiles) {
|
|
DeviceVector sourceDevices;
|
|
for (const auto& route : stream->getRoutes()) {
|
|
sp<PolicyAudioPort> sink = route->getSink();
|
|
if (sink == 0 || stream != sink) {
|
|
ALOGE("%s: Invalid route attached to input stream", __FUNCTION__);
|
|
continue;
|
|
}
|
|
DeviceVector sourceDevicesForRoute = getRouteSourceDevices(route);
|
|
if (sourceDevicesForRoute.isEmpty()) {
|
|
ALOGE("%s: invalid source devices for %s", __FUNCTION__, stream->getName().c_str());
|
|
continue;
|
|
}
|
|
sourceDevices.add(sourceDevicesForRoute);
|
|
}
|
|
if (sourceDevices.isEmpty()) {
|
|
ALOGE("%s: invalid source devices for %s", __FUNCTION__, stream->getName().c_str());
|
|
continue;
|
|
}
|
|
stream->setSupportedDevices(sourceDevices);
|
|
}
|
|
for (const auto& stream : mOutputProfiles) {
|
|
DeviceVector sinkDevices;
|
|
for (const auto& route : stream->getRoutes()) {
|
|
sp<PolicyAudioPort> source = findByTagName(route->getSources(), stream->getTagName());
|
|
if (source == 0 || stream != source) {
|
|
ALOGE("%s: Invalid route attached to output stream", __FUNCTION__);
|
|
continue;
|
|
}
|
|
sp<DeviceDescriptor> sinkDevice = getRouteSinkDevice(route);
|
|
if (sinkDevice == 0) {
|
|
ALOGE("%s: invalid sink device for %s", __FUNCTION__, stream->getName().c_str());
|
|
continue;
|
|
}
|
|
sinkDevices.add(sinkDevice);
|
|
}
|
|
stream->setSupportedDevices(sinkDevices);
|
|
}
|
|
}
|
|
|
|
void HwModule::setHandle(audio_module_handle_t handle) {
|
|
ALOGW_IF(mHandle != AUDIO_MODULE_HANDLE_NONE,
|
|
"HwModule handle is changing from %d to %d", mHandle, handle);
|
|
mHandle = handle;
|
|
}
|
|
|
|
bool HwModule::supportsPatch(const sp<PolicyAudioPort> &srcPort,
|
|
const sp<PolicyAudioPort> &dstPort) const {
|
|
for (const auto &route : mRoutes) {
|
|
if (route->supportsPatch(srcPort, dstPort)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void HwModule::dump(String8 *dst, int spaces) const
|
|
{
|
|
dst->appendFormat("Handle: %d; \"%s\"\n", mHandle, getName());
|
|
if (mOutputProfiles.size()) {
|
|
dst->appendFormat("%*s- Output MixPorts (%zu):\n", spaces - 2, "", mOutputProfiles.size());
|
|
for (size_t i = 0; i < mOutputProfiles.size(); i++) {
|
|
const std::string prefix = base::StringPrintf("%*s %zu. ", spaces, "", i + 1);
|
|
dst->append(prefix.c_str());
|
|
mOutputProfiles[i]->dump(dst, prefix.size());
|
|
}
|
|
}
|
|
if (mInputProfiles.size()) {
|
|
dst->appendFormat("%*s- Input MixPorts (%zu):\n", spaces - 2, "", mInputProfiles.size());
|
|
for (size_t i = 0; i < mInputProfiles.size(); i++) {
|
|
const std::string prefix = base::StringPrintf("%*s %zu. ", spaces, "", i + 1);
|
|
dst->append(prefix.c_str());
|
|
mInputProfiles[i]->dump(dst, prefix.size());
|
|
}
|
|
}
|
|
mDeclaredDevices.dump(dst, String8("- Declared"), spaces - 2, true);
|
|
mDynamicDevices.dump(dst, String8("- Dynamic"), spaces - 2, true);
|
|
dumpAudioRouteVector(mRoutes, dst, spaces);
|
|
}
|
|
|
|
sp <HwModule> HwModuleCollection::getModuleFromName(const char *name) const
|
|
{
|
|
for (const auto& module : *this) {
|
|
if (strcmp(module->getName(), name) == 0) {
|
|
return module;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
sp<HwModule> HwModuleCollection::getModuleForDeviceType(audio_devices_t type,
|
|
audio_format_t encodedFormat,
|
|
std::string *tagName) const
|
|
{
|
|
for (const auto& module : *this) {
|
|
const auto& profiles = audio_is_output_device(type) ?
|
|
module->getOutputProfiles() : module->getInputProfiles();
|
|
for (const auto& profile : profiles) {
|
|
if (profile->supportsDeviceTypes({type})) {
|
|
if (encodedFormat != AUDIO_FORMAT_DEFAULT) {
|
|
DeviceVector declaredDevices = module->getDeclaredDevices();
|
|
sp <DeviceDescriptor> deviceDesc =
|
|
declaredDevices.getDevice(type, String8(), encodedFormat);
|
|
if (deviceDesc) {
|
|
if (tagName != nullptr) {
|
|
*tagName = deviceDesc->getTagName();
|
|
}
|
|
return module;
|
|
}
|
|
} else {
|
|
if (tagName != nullptr) {
|
|
*tagName = profile->getTag({type});
|
|
}
|
|
return module;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
sp<HwModule> HwModuleCollection::getModuleForDevice(const sp<DeviceDescriptor> &device,
|
|
audio_format_t encodedFormat) const
|
|
{
|
|
return getModuleForDeviceType(device->type(), encodedFormat);
|
|
}
|
|
|
|
DeviceVector HwModuleCollection::getAvailableDevicesFromModuleName(
|
|
const char *name, const DeviceVector &availableDevices) const
|
|
{
|
|
sp<HwModule> module = getModuleFromName(name);
|
|
if (module == nullptr) {
|
|
return DeviceVector();
|
|
}
|
|
return availableDevices.getDevicesFromHwModule(module->getHandle());
|
|
}
|
|
|
|
sp<DeviceDescriptor> HwModuleCollection::getDeviceDescriptor(const audio_devices_t deviceType,
|
|
const char *address,
|
|
const char *name,
|
|
const audio_format_t encodedFormat,
|
|
bool allowToCreate,
|
|
bool matchAddress) const
|
|
{
|
|
String8 devAddress = (address == nullptr || !matchAddress) ? String8("") : String8(address);
|
|
// handle legacy remote submix case where the address was not always specified
|
|
if (audio_is_remote_submix_device(deviceType) && (devAddress.length() == 0)) {
|
|
devAddress = String8("0");
|
|
}
|
|
|
|
for (const auto& hwModule : *this) {
|
|
if (!allowToCreate) {
|
|
auto dynamicDevices = hwModule->getDynamicDevices();
|
|
auto dynamicDevice = dynamicDevices.getDevice(deviceType, devAddress, encodedFormat);
|
|
if (dynamicDevice) {
|
|
return dynamicDevice;
|
|
}
|
|
}
|
|
DeviceVector moduleDevices = hwModule->getAllDevices();
|
|
auto moduleDevice = moduleDevices.getDevice(deviceType, devAddress, encodedFormat);
|
|
|
|
// Prevent overwritting moduleDevice address if connected device does not have the same
|
|
// address (since getDevice with empty address ignores match on address), use dynamic device
|
|
if (moduleDevice && allowToCreate &&
|
|
(!moduleDevice->address().empty() &&
|
|
(moduleDevice->address().compare(devAddress.c_str()) != 0))) {
|
|
break;
|
|
}
|
|
if (moduleDevice) {
|
|
if (encodedFormat != AUDIO_FORMAT_DEFAULT) {
|
|
moduleDevice->setEncodedFormat(encodedFormat);
|
|
}
|
|
if (allowToCreate) {
|
|
moduleDevice->attach(hwModule);
|
|
// Name may be overwritten, restored on detach.
|
|
moduleDevice->setAddress(devAddress.string());
|
|
// Name may be overwritten, restored on detach.
|
|
moduleDevice->setName(name);
|
|
}
|
|
return moduleDevice;
|
|
}
|
|
}
|
|
if (!allowToCreate) {
|
|
ALOGV("%s: could not find HW module for device %s %04x address %s", __FUNCTION__,
|
|
name, deviceType, address);
|
|
return nullptr;
|
|
}
|
|
return createDevice(deviceType, address, name, encodedFormat);
|
|
}
|
|
|
|
sp<DeviceDescriptor> HwModuleCollection::createDevice(const audio_devices_t type,
|
|
const char *address,
|
|
const char *name,
|
|
const audio_format_t encodedFormat) const
|
|
{
|
|
std::string tagName = {};
|
|
sp<HwModule> hwModule = getModuleForDeviceType(type, encodedFormat, &tagName);
|
|
if (hwModule == 0) {
|
|
ALOGE("%s: could not find HW module for device %04x address %s", __FUNCTION__, type,
|
|
address);
|
|
return nullptr;
|
|
}
|
|
|
|
sp<DeviceDescriptor> device = new DeviceDescriptor(type, tagName, address);
|
|
device->setName(name);
|
|
device->setEncodedFormat(encodedFormat);
|
|
device->setDynamic();
|
|
// Add the device to the list of dynamic devices
|
|
hwModule->addDynamicDevice(device);
|
|
// Reciprocally attach the device to the module
|
|
device->attach(hwModule);
|
|
ALOGD("%s: adding dynamic device %s to module %s", __FUNCTION__,
|
|
device->toString().c_str(), hwModule->getName());
|
|
|
|
const auto &profiles = (audio_is_output_device(type) ? hwModule->getOutputProfiles() :
|
|
hwModule->getInputProfiles());
|
|
for (const auto &profile : profiles) {
|
|
// Add the device as supported to all profile supporting "weakly" or not the device
|
|
// according to its type
|
|
if (profile->supportsDevice(device, false /*matchAddress*/)) {
|
|
|
|
// @todo quid of audio profile? import the profile from device of the same type?
|
|
const auto &isoTypeDeviceForProfile =
|
|
profile->getSupportedDevices().getDevice(type, String8(), AUDIO_FORMAT_DEFAULT);
|
|
device->importAudioPortAndPickAudioProfile(isoTypeDeviceForProfile, true /* force */);
|
|
|
|
ALOGV("%s: adding device %s to profile %s", __FUNCTION__,
|
|
device->toString().c_str(), profile->getTagName().c_str());
|
|
profile->addSupportedDevice(device);
|
|
}
|
|
}
|
|
return device;
|
|
}
|
|
|
|
void HwModuleCollection::cleanUpForDevice(const sp<DeviceDescriptor> &device)
|
|
{
|
|
for (const auto& hwModule : *this) {
|
|
DeviceVector moduleDevices = hwModule->getAllDevices();
|
|
if (!moduleDevices.contains(device)) {
|
|
continue;
|
|
}
|
|
|
|
// removal of remote submix devices associated with a dynamic policy is
|
|
// handled by removeOutputProfile() and removeInputProfile()
|
|
if (audio_is_remote_submix_device(device->type()) && device->address() != "0") {
|
|
continue;
|
|
}
|
|
|
|
device->detach();
|
|
// Only remove from dynamic list, not from declared list!!!
|
|
if (!hwModule->removeDynamicDevice(device)) {
|
|
return;
|
|
}
|
|
ALOGV("%s: removed dynamic device %s from module %s", __FUNCTION__,
|
|
device->toString().c_str(), hwModule->getName());
|
|
|
|
const IOProfileCollection &profiles = audio_is_output_device(device->type()) ?
|
|
hwModule->getOutputProfiles() : hwModule->getInputProfiles();
|
|
for (const auto &profile : profiles) {
|
|
// For cleanup, strong match is required
|
|
if (profile->supportsDevice(device, true /*matchAdress*/)) {
|
|
ALOGV("%s: removing device %s from profile %s", __FUNCTION__,
|
|
device->toString().c_str(), profile->getTagName().c_str());
|
|
profile->removeSupportedDevice(device);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HwModuleCollection::dump(String8 *dst) const
|
|
{
|
|
dst->appendFormat("\n Hardware modules (%zu):\n", size());
|
|
for (size_t i = 0; i < size(); i++) {
|
|
const std::string prefix = base::StringPrintf(" %zu. ", i + 1);
|
|
dst->append(prefix.c_str());
|
|
itemAt(i)->dump(dst, prefix.size());
|
|
}
|
|
}
|
|
|
|
|
|
} //namespace android
|