libaudiohal@aidl: Properly handle "legacy" r_submix case
The "legacy" r_submix case is the "default" remote submix
device with the address "0". For backwards compatibility,
the input for this device must be attached. The output
is still opened dynamically. However, the framework expects
to see a declared device with address "0" and some profiles.
Added code to libaudiohal@aidl to perform this adaptation.
Also, found that `DeviceDescriptor` was missing setting
`mDeclaredAddress` when loading from a parcelable.
Bug: 302036943
Test: atest audioeffect_analysis
Test: atest CtsMediaAudioTestCases --test-filter=".*AudioPlaybackCaptureTest.*"
Change-Id: I28294cbb2895e648fe962feb3e9a0645fc823153
diff --git a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
index 501831d..2ab8053 100644
--- a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
+++ b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
@@ -79,8 +79,8 @@
bool equals(const sp<DeviceDescriptorBase>& other) const;
- status_t writeToParcelable(media::AudioPortFw* parcelable) const;
- status_t readFromParcelable(const media::AudioPortFw& parcelable);
+ virtual status_t writeToParcelable(media::AudioPortFw* parcelable) const;
+ virtual status_t readFromParcelable(const media::AudioPortFw& parcelable);
protected:
AudioDeviceTypeAddr mDeviceTypeAddr;
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index e2233c7..5bc25ae 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -28,6 +28,7 @@
#include <media/AidlConversionNdkCpp.h>
#include <media/AidlConversionUtil.h>
#include <mediautils/TimeCheck.h>
+#include <system/audio.h>
#include <Utils.h>
#include <utils/Log.h>
@@ -41,6 +42,7 @@
using aidl::android::media::audio::common::AudioConfig;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceAddress;
+using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
@@ -185,23 +187,77 @@
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->getAudioPorts(&ports)));
ALOGW_IF(ports.empty(), "%s: module %s returned an empty list of audio ports",
__func__, mInstance.c_str());
- std::transform(ports.begin(), ports.end(), std::inserter(mPorts, mPorts.end()),
- [](const auto& p) { return std::make_pair(p.id, p); });
mDefaultInputPortId = mDefaultOutputPortId = -1;
const int defaultDeviceFlag = 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE;
- for (const auto& pair : mPorts) {
- const auto& p = pair.second;
- if (p.ext.getTag() == AudioPortExt::Tag::device &&
- (p.ext.get<AudioPortExt::Tag::device>().flags & defaultDeviceFlag) != 0) {
- if (p.flags.getTag() == AudioIoFlags::Tag::input) {
- mDefaultInputPortId = p.id;
- } else if (p.flags.getTag() == AudioIoFlags::Tag::output) {
- mDefaultOutputPortId = p.id;
+ for (auto it = ports.begin(); it != ports.end(); ) {
+ const auto& port = *it;
+ if (port.ext.getTag() != AudioPortExt::Tag::device) {
+ ++it;
+ continue;
+ }
+ const AudioPortDeviceExt& deviceExt = port.ext.get<AudioPortExt::Tag::device>();
+ if ((deviceExt.flags & defaultDeviceFlag) != 0) {
+ if (port.flags.getTag() == AudioIoFlags::Tag::input) {
+ mDefaultInputPortId = port.id;
+ } else if (port.flags.getTag() == AudioIoFlags::Tag::output) {
+ mDefaultOutputPortId = port.id;
}
}
+ // For compatibility with HIDL, hide "template" remote submix ports from ports list.
+ if (const auto& devDesc = deviceExt.device;
+ (devDesc.type.type == AudioDeviceType::IN_SUBMIX ||
+ devDesc.type.type == AudioDeviceType::OUT_SUBMIX) &&
+ devDesc.type.connection == AudioDeviceDescription::CONNECTION_VIRTUAL) {
+ if (devDesc.type.type == AudioDeviceType::IN_SUBMIX) {
+ mRemoteSubmixIn = port;
+ } else {
+ mRemoteSubmixOut = port;
+ }
+ it = ports.erase(it);
+ } else {
+ ++it;
+ }
}
+ if (mRemoteSubmixIn.has_value() != mRemoteSubmixOut.has_value()) {
+ ALOGE("%s: The configuration only has input or output remote submix device, must have both",
+ __func__);
+ mRemoteSubmixIn.reset();
+ mRemoteSubmixOut.reset();
+ }
+ if (mRemoteSubmixIn.has_value()) {
+ AudioPort connectedRSubmixIn = *mRemoteSubmixIn;
+ connectedRSubmixIn.ext.get<AudioPortExt::Tag::device>().device.address =
+ AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS;
+ ALOGD("%s: connecting remote submix input", __func__);
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
+ connectedRSubmixIn, &connectedRSubmixIn)));
+ // The template port for the remote submix input couldn't be "default" because it is not
+ // attached. The connected port can now be made default because we never disconnect it.
+ if (mDefaultInputPortId == -1) {
+ mDefaultInputPortId = connectedRSubmixIn.id;
+ }
+ ports.push_back(std::move(connectedRSubmixIn));
+
+ // Remote submix output must not be connected until the framework actually starts
+ // using it, however for legacy compatibility we need to provide an "augmented template"
+ // port with an address and profiles. It is obtained by connecting the output and then
+ // immediately disconnecting it. This is a cheap operation as we don't open any streams.
+ AudioPort tempConnectedRSubmixOut = *mRemoteSubmixOut;
+ tempConnectedRSubmixOut.ext.get<AudioPortExt::Tag::device>().device.address =
+ AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS;
+ ALOGD("%s: temporarily connecting and disconnecting remote submix output", __func__);
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
+ tempConnectedRSubmixOut, &tempConnectedRSubmixOut)));
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->disconnectExternalDevice(
+ tempConnectedRSubmixOut.id)));
+ tempConnectedRSubmixOut.id = mRemoteSubmixOut->id;
+ ports.push_back(std::move(tempConnectedRSubmixOut));
+ }
+
ALOGI("%s: module %s default port ids: input %d, output %d",
__func__, mInstance.c_str(), mDefaultInputPortId, mDefaultOutputPortId);
+ std::transform(ports.begin(), ports.end(), std::inserter(mPorts, mPorts.end()),
+ [](const auto& p) { return std::make_pair(p.id, p); });
RETURN_STATUS_IF_ERROR(updateRoutes());
std::vector<AudioPortConfig> portConfigs;
RETURN_STATUS_IF_ERROR(
@@ -1079,6 +1135,7 @@
}
status_t DeviceHalAidl::setConnectedState(const struct audio_port_v7 *port, bool connected) {
+ ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mModule) return NO_INIT;
if (port == nullptr) {
@@ -1104,20 +1161,43 @@
}
if (connected) {
AudioDevice matchDevice = aidlPort.ext.get<AudioPortExt::device>().device;
- // Reset the device address to find the "template" port.
- matchDevice.address = AudioDeviceAddress::make<AudioDeviceAddress::id>();
- auto portsIt = findPort(matchDevice);
- if (portsIt == mPorts.end()) {
- // Since 'setConnectedState' is called for all modules, it is normal when the device
- // port not found in every one of them.
- return BAD_VALUE;
+ std::optional<AudioPort> templatePort;
+ auto erasePortAfterConnectionIt = mPorts.end();
+ // Connection of remote submix out with address "0" is a special case. Since there is
+ // already an "augmented template" port with this address in mPorts, we need to replace
+ // it with a connected port.
+ // Connection of remote submix outs with any other address is done as usual except that
+ // the template port is in `mRemoteSubmixOut`.
+ if (mRemoteSubmixOut.has_value() &&
+ matchDevice.type.type == AudioDeviceType::OUT_SUBMIX) {
+ if (matchDevice.address == AudioDeviceAddress::make<AudioDeviceAddress::id>(
+ AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS)) {
+ erasePortAfterConnectionIt = findPort(matchDevice);
+ }
+ templatePort = mRemoteSubmixOut;
+ } else if (mRemoteSubmixIn.has_value() &&
+ matchDevice.type.type == AudioDeviceType::IN_SUBMIX) {
+ templatePort = mRemoteSubmixIn;
} else {
- ALOGD("%s: device port for device %s found in the module %s",
- __func__, matchDevice.toString().c_str(), mInstance.c_str());
+ // Reset the device address to find the "template" port.
+ matchDevice.address = AudioDeviceAddress::make<AudioDeviceAddress::id>();
+ }
+ if (!templatePort.has_value()) {
+ auto portsIt = findPort(matchDevice);
+ if (portsIt == mPorts.end()) {
+ // Since 'setConnectedState' is called for all modules, it is normal when the device
+ // port not found in every one of them.
+ return BAD_VALUE;
+ } else {
+ ALOGD("%s: device port for device %s found in the module %s",
+ __func__, matchDevice.toString().c_str(), mInstance.c_str());
+ }
+ templatePort = portsIt->second;
}
resetUnusedPatchesAndPortConfigs();
+
// Use the ID of the "template" port, use all the information from the provided port.
- aidlPort.id = portsIt->first;
+ aidlPort.id = templatePort->id;
AudioPort connectedPort;
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
aidlPort, &connectedPort)));
@@ -1127,6 +1207,9 @@
__func__, mInstance.c_str(), connectedPort.toString().c_str(),
it->second.toString().c_str());
mConnectedPorts[connectedPort.id] = false;
+ if (erasePortAfterConnectionIt != mPorts.end()) {
+ mPorts.erase(erasePortAfterConnectionIt);
+ }
} else { // !connected
AudioDevice matchDevice = aidlPort.ext.get<AudioPortExt::device>().device;
auto portsIt = findPort(matchDevice);
@@ -1139,14 +1222,24 @@
__func__, matchDevice.toString().c_str(), mInstance.c_str());
}
resetUnusedPatchesAndPortConfigs();
+
+ // Disconnection of remote submix out with address "0" is a special case. We need to replace
+ // the connected port entry with the "augmented template".
+ const int32_t portId = portsIt->second.id;
+ if (mRemoteSubmixOut.has_value() && matchDevice.type.type == AudioDeviceType::OUT_SUBMIX &&
+ matchDevice.address == AudioDeviceAddress::make<AudioDeviceAddress::id>(
+ AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS)) {
+ mDisconnectedPortReplacement = std::make_pair(portId, *mRemoteSubmixOut);
+ auto& port = mDisconnectedPortReplacement.second;
+ port.ext.get<AudioPortExt::Tag::device>().device = matchDevice;
+ port.profiles = portsIt->second.profiles;
+ }
// Streams are closed by AudioFlinger independently from device disconnections.
// It is possible that the stream has not been closed yet.
- const int32_t portId = portsIt->second.id;
if (!isPortHeldByAStream(portId)) {
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
mModule->disconnectExternalDevice(portId)));
- mPorts.erase(portsIt);
- mConnectedPorts.erase(portId);
+ eraseConnectedPort(portId);
} else {
ALOGD("%s: since device port ID %d is used by a stream, "
"external device disconnection postponed", __func__, portId);
@@ -1216,6 +1309,17 @@
return OK;
}
+void DeviceHalAidl::eraseConnectedPort(int32_t portId) {
+ mPorts.erase(portId);
+ mConnectedPorts.erase(portId);
+ if (mDisconnectedPortReplacement.first == portId) {
+ const auto& port = mDisconnectedPortReplacement.second;
+ mPorts.insert(std::make_pair(port.id, port));
+ ALOGD("%s: disconnected port replacement: %s", __func__, port.toString().c_str());
+ mDisconnectedPortReplacement = std::pair<int32_t, AudioPort>();
+ }
+}
+
status_t DeviceHalAidl::filterAndRetrieveBtA2dpParameters(
AudioParameter &keys, AudioParameter *result) {
TIME_CHECK();
@@ -1847,8 +1951,7 @@
if (!isPortHeldByAStream(portId)) {
TIME_CHECK();
if (auto status = mModule->disconnectExternalDevice(portId); status.isOk()) {
- mPorts.erase(portId);
- mConnectedPorts.erase(portId);
+ eraseConnectedPort(portId);
ALOGD("%s: executed postponed external device disconnection for port ID %d",
__func__, portId);
}
@@ -1865,6 +1968,30 @@
statusTFromBinderStatus(mModule->getAudioRoutes(&mRoutes)));
ALOGW_IF(mRoutes.empty(), "%s: module %s returned an empty list of audio routes",
__func__, mInstance.c_str());
+ if (mRemoteSubmixIn.has_value()) {
+ // Remove mentions of the template remote submix input from routes.
+ int32_t rSubmixInId = mRemoteSubmixIn->id;
+ // Remove mentions of the template remote submix out only if it is not in mPorts
+ // (that means there is a connected port in mPorts).
+ int32_t rSubmixOutId = mPorts.find(mRemoteSubmixOut->id) == mPorts.end() ?
+ mRemoteSubmixOut->id : -1;
+ for (auto it = mRoutes.begin(); it != mRoutes.end();) {
+ auto& route = *it;
+ if (route.sinkPortId == rSubmixOutId) {
+ it = mRoutes.erase(it);
+ continue;
+ }
+ if (auto sourceIt = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(),
+ rSubmixInId); sourceIt != route.sourcePortIds.end()) {
+ route.sourcePortIds.erase(sourceIt);
+ if (route.sourcePortIds.empty()) {
+ it = mRoutes.erase(it);
+ continue;
+ }
+ }
+ ++it;
+ }
+ }
mRoutingMatrix.clear();
for (const auto& r : mRoutes) {
for (auto portId : r.sourcePortIds) {
diff --git a/media/libaudiohal/impl/DeviceHalAidl.h b/media/libaudiohal/impl/DeviceHalAidl.h
index e1fe4d1..99e8503 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.h
+++ b/media/libaudiohal/impl/DeviceHalAidl.h
@@ -223,6 +223,7 @@
status_t createOrUpdatePortConfig(
const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
PortConfigs::iterator* result, bool *created);
+ void eraseConnectedPort(int32_t portId);
status_t filterAndRetrieveBtA2dpParameters(AudioParameter &keys, AudioParameter *result);
status_t filterAndUpdateBtA2dpParameters(AudioParameter ¶meters);
status_t filterAndUpdateBtHfpParameters(AudioParameter ¶meters);
@@ -313,6 +314,10 @@
std::shared_ptr<::aidl::android::hardware::audio::core::sounddose::ISoundDose>
mSoundDose = nullptr;
Ports mPorts;
+ // Remote submix "template" ports (no address specified, no profiles).
+ // They are excluded from `mPorts` as their presence confuses the framework code.
+ std::optional<::aidl::android::media::audio::common::AudioPort> mRemoteSubmixIn;
+ std::optional<::aidl::android::media::audio::common::AudioPort> mRemoteSubmixOut;
int32_t mDefaultInputPortId = -1;
int32_t mDefaultOutputPortId = -1;
PortConfigs mPortConfigs;
@@ -326,6 +331,8 @@
std::map<void*, Callbacks> mCallbacks GUARDED_BY(mLock);
std::set<audio_port_handle_t> mDeviceDisconnectionNotified;
ConnectedPorts mConnectedPorts;
+ std::pair<int32_t, ::aidl::android::media::audio::common::AudioPort>
+ mDisconnectedPortReplacement;
};
} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index 80e098b..6c130fd 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -89,6 +89,8 @@
void importAudioPortAndPickAudioProfile(const sp<PolicyAudioPort>& policyPort,
bool force = false);
+ status_t readFromParcelable(const media::AudioPortFw& parcelable) override;
+
void setEncapsulationInfoFromHal(AudioPolicyClientInterface *clientInterface);
void dump(String8 *dst, int spaces, bool verbose = true) const;
@@ -104,7 +106,7 @@
std::string mTagName; // Unique human readable identifier for a device port found in conf file.
audio_format_t mCurrentEncodedFormat;
bool mIsDynamic = false;
- const std::string mDeclaredAddress; // Original device address
+ std::string mDeclaredAddress; // Original device address
};
class DeviceVector : public SortedVector<sp<DeviceDescriptor> >
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyConfig.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyConfig.cpp
index 8c7a7de..4edd11f 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyConfig.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyConfig.cpp
@@ -29,6 +29,8 @@
namespace android {
+using media::audio::common::AudioDeviceAddress;
+using media::audio::common::AudioDeviceType;
using media::audio::common::AudioIoFlags;
using media::audio::common::AudioPortDeviceExt;
using media::audio::common::AudioPortExt;
@@ -113,12 +115,17 @@
ports.emplace(aidlPort.id, devicePort);
if (const auto& deviceExt = aidlPort.ext.get<AudioPortExt::device>();
- deviceExt.device.type.connection.empty()) { // Attached device
+ deviceExt.device.type.connection.empty() ||
+ // DeviceHalAidl connects remote submix input with an address.
+ (deviceExt.device.type.type == AudioDeviceType::IN_SUBMIX &&
+ deviceExt.device.address != AudioDeviceAddress())) {
+ // Attached device.
if (isInput) {
attachedInputDevices->add(devicePort);
} else {
attachedOutputDevices->add(devicePort);
- if ((deviceExt.flags & defaultDeviceFlag) != 0) {
+ if (*defaultOutputDevice == nullptr &&
+ (deviceExt.flags & defaultDeviceFlag) != 0) {
*defaultOutputDevice = devicePort;
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 514601c..fe25693 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -154,6 +154,12 @@
policyPort->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
}
+status_t DeviceDescriptor::readFromParcelable(const media::AudioPortFw& parcelable) {
+ RETURN_STATUS_IF_ERROR(DeviceDescriptorBase::readFromParcelable(parcelable));
+ mDeclaredAddress = DeviceDescriptorBase::address();
+ return OK;
+}
+
void DeviceDescriptor::setEncapsulationInfoFromHal(
AudioPolicyClientInterface *clientInterface) {
AudioParameter param(String8(mDeviceTypeAddr.getAddress()));