audiopolicy: effects: move to orphans when disconnected device
Bug: 267799634
Test: atest AudioPolicyManagerPreProcEffectTest#DeviceDisconnectWhileClientActive
Session pre processing effects are attached to an input.
The device on which is routed this input is disconnected.
The input is closed, effects are still pointing on dead input
io handle.
The client tried to get another input (or device is reconnected),
the session is unchanged, thus, it tries to re associat effects.
It crashes since the associated input to move from has disappeared.
This CL fixes this issue by moving to orphan (aka resetting associated io
handle) all the effects for an input while closed.
Change-Id: Ie490d93c48a4ea71f3f6ff716eca615eebf2efdc
Signed-off-by: François Gaffie <francois.gaffie@renault.com>
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index d40bbcb..c2e4b11 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -84,6 +84,7 @@
const AudioInputCollection *inputs, AudioPolicyClientInterface *mClientInterface);
void putOrphanEffects(audio_session_t sessionId, audio_io_handle_t srcIo,
const AudioInputCollection *inputs, AudioPolicyClientInterface *clientInterface);
+ void putOrphanEffectsForIo(audio_io_handle_t srcIo);
/**
* @brief Checks if an effect session was already attached to an io handle and return it if
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index 1f6946c..c85df0f 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -302,6 +302,17 @@
}
}
+void EffectDescriptorCollection::putOrphanEffectsForIo(audio_io_handle_t srcIo)
+{
+ for (size_t i = 0; i < size(); i++) {
+ sp<EffectDescriptor> effect = valueAt(i);
+ if (effect->mIo == srcIo) {
+ effect->mIo = AUDIO_IO_HANDLE_NONE;
+ effect->mIsOrphan = true;
+ }
+ }
+}
+
void EffectDescriptorCollection::putOrphanEffects(audio_session_t session,
audio_io_handle_t srcIo, const AudioInputCollection *inputs,
AudioPolicyClientInterface *clientInterface)
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 884ad96..26eb5ec 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -6769,6 +6769,7 @@
mpClientInterface->onAudioPatchListUpdate();
}
+ mEffects.putOrphanEffectsForIo(input);
inputDesc->close();
mInputs.removeItem(input);
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 7a391b6..110e6bf 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -26,7 +26,9 @@
#define LOG_TAG "APM_Test"
#include <Serializer.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android/content/AttributionSourceState.h>
+#include <hardware/audio_effect.h>
#include <media/AudioPolicy.h>
#include <media/PatchBuilder.h>
#include <media/RecordingActivityTracker.h>
@@ -185,6 +187,7 @@
bool* isBitPerfect = nullptr);
void getInputForAttr(
const audio_attributes_t &attr,
+ audio_io_handle_t *input,
audio_session_t session,
audio_unique_id_t riid,
audio_port_handle_t *selectedDeviceId,
@@ -296,6 +299,7 @@
void AudioPolicyManagerTest::getInputForAttr(
const audio_attributes_t &attr,
+ audio_io_handle_t *input,
const audio_session_t session,
audio_unique_id_t riid,
audio_port_handle_t *selectedDeviceId,
@@ -304,7 +308,6 @@
int sampleRate,
audio_input_flags_t flags,
audio_port_handle_t *portId) {
- audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
config.sample_rate = sampleRate;
config.channel_mask = channelMask;
@@ -315,7 +318,7 @@
AudioPolicyInterface::input_type_t inputType;
AttributionSourceState attributionSource = createAttributionSourceState(/*uid=*/ 0);
ASSERT_EQ(OK, mManager->getInputForAttr(
- &attr, &input, riid, session, attributionSource, &config, flags,
+ &attr, input, riid, session, attributionSource, &config, flags,
selectedDeviceId, &inputType, portId));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
}
@@ -945,10 +948,13 @@
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t mixPortId = AUDIO_PORT_HANDLE_NONE;
audio_source_t source = AUDIO_SOURCE_VOICE_COMMUNICATION;
- audio_attributes_t attr = {
- AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source, AUDIO_FLAG_NONE, ""};
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, 8000, AUDIO_INPUT_FLAG_VOIP_TX, &mixPortId));
+ audio_attributes_t attr = {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source,
+ AUDIO_FLAG_NONE, ""};
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1,
+ &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_IN_MONO, 8000, AUDIO_INPUT_FLAG_VOIP_TX,
+ &mixPortId));
std::vector<audio_port_v7> ports;
ASSERT_NO_FATAL_FAILURE(
@@ -1708,10 +1714,11 @@
audio_attributes_t attr = {
AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source, AUDIO_FLAG_NONE, ""};
std::string tags = "addr=" + mMixAddress;
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- getInputForAttr(attr, param.session, mTracker->getRiid(), &selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate,
- AUDIO_INPUT_FLAG_NONE, &mPortId);
+ getInputForAttr(attr, &input, param.session, mTracker->getRiid(),
+ &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &mPortId);
ASSERT_EQ(NO_ERROR, mManager->startInput(mPortId));
ASSERT_EQ(extractionPort.id, selectedDeviceId);
@@ -2051,9 +2058,10 @@
audio_port_handle_t captureRoutedPortId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
- getInputForAttr(param.attributes, param.session, mTracker->getRiid(), &captureRoutedPortId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate,
- AUDIO_INPUT_FLAG_NONE, &portId);
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ getInputForAttr(param.attributes, &input, param.session, mTracker->getRiid(),
+ &captureRoutedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &portId);
if (param.expected_match) {
EXPECT_EQ(mExtractionPort.id, captureRoutedPortId);
} else {
@@ -2236,9 +2244,10 @@
k48000SamplingRate, AUDIO_OUTPUT_FLAG_NONE);
} else if (audio_is_input_device(type)) {
RecordingActivityTracker tracker;
- getInputForAttr({}, AUDIO_SESSION_NONE, tracker.getRiid(), &routedPortId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate,
- AUDIO_INPUT_FLAG_NONE);
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ getInputForAttr({}, &input, AUDIO_SESSION_NONE, tracker.getRiid(), &routedPortId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate,
+ AUDIO_INPUT_FLAG_NONE);
}
ASSERT_EQ(devicePort.id, routedPortId);
@@ -2985,7 +2994,8 @@
audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
attr.source = source;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
auto selectedDevice = availableDevices.getDeviceFromId(selectedDeviceId);
@@ -3005,7 +3015,8 @@
mManager->setDevicesRoleForCapturePreset(source, role,
{preferredDevice->getDeviceTypeAddr()}));
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
ASSERT_EQ(preferredDevice, availableDevices.getDeviceFromId(selectedDeviceId));
@@ -3015,7 +3026,8 @@
ASSERT_EQ(NO_ERROR,
mManager->clearDevicesRoleForCapturePreset(source, role));
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
ASSERT_EQ(selectedDevice, availableDevices.getDeviceFromId(selectedDeviceId));
@@ -3040,7 +3052,8 @@
audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
attr.source = source;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
auto selectedDevice = availableDevices.getDeviceFromId(selectedDeviceId);
@@ -3052,9 +3065,10 @@
mManager->setDevicesRoleForCapturePreset(source, role,
{selectedDevice->getDeviceTypeAddr()}));
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
- 48000));
+ input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1,
+ &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_IN_STEREO, 48000));
ASSERT_NE(selectedDevice, availableDevices.getDeviceFromId(selectedDeviceId));
// After clearing disabled device for capture preset, the selected device for input should be
@@ -3062,7 +3076,8 @@
ASSERT_EQ(NO_ERROR,
mManager->clearDevicesRoleForCapturePreset(source, role));
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
+ input = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input, AUDIO_SESSION_NONE, 1, &selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
48000));
ASSERT_EQ(selectedDevice, availableDevices.getDeviceFromId(selectedDeviceId));
@@ -3098,3 +3113,77 @@
DevicesRoleForCapturePresetParam({AUDIO_SOURCE_HOTWORD, DEVICE_ROLE_PREFERRED})
)
);
+
+
+const effect_descriptor_t TEST_EFFECT_DESC = {
+ {0xf2a4bb20, 0x0c3c, 0x11e3, 0x8b07, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
+ {0xff93e360, 0x0c3c, 0x11e3, 0x8a97, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ EFFECT_FLAG_TYPE_PRE_PROC,
+ 0,
+ 1,
+ "APM test Effect",
+ "The Android Open Source Project",
+};
+
+class AudioPolicyManagerPreProcEffectTest : public AudioPolicyManagerTestWithConfigurationFile {
+};
+
+TEST_F(AudioPolicyManagerPreProcEffectTest, DeviceDisconnectWhileClientActive) {
+ const audio_source_t source = AUDIO_SOURCE_MIC;
+ const std::string address = "BUS00_MIC";
+ const std::string deviceName = "randomName";
+ audio_port_handle_t portId;
+ audio_devices_t type = AUDIO_DEVICE_IN_BUS;
+
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(type,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.c_str(), deviceName.c_str(),
+ AUDIO_FORMAT_DEFAULT));
+ auto availableDevices = mManager->getAvailableInputDevices();
+ ASSERT_GT(availableDevices.size(), 1);
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = source;
+ audio_session_t session = TEST_SESSION_ID;
+ audio_io_handle_t inputClientHandle = 777;
+ int effectId = 666;
+ audio_port_v7 devicePort;
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, type, address, &devicePort));
+
+ audio_port_handle_t routedPortId = devicePort.id;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &inputClientHandle, session, 1, &routedPortId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000, AUDIO_INPUT_FLAG_NONE, &portId));
+ ASSERT_EQ(devicePort.id, routedPortId);
+ auto selectedDevice = availableDevices.getDeviceFromId(routedPortId);
+ ASSERT_NE(nullptr, selectedDevice);
+
+ // Add a pre processing effect on the input client session
+ ASSERT_EQ(NO_ERROR, mManager->registerEffect(&TEST_EFFECT_DESC, inputClientHandle,
+ PRODUCT_STRATEGY_NONE, session, effectId));
+
+ ASSERT_EQ(NO_ERROR, mManager->startInput(portId));
+
+ // Force a device disconnection to close the input, no crash expected of APM
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ type, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ address.c_str(), deviceName.c_str(), AUDIO_FORMAT_DEFAULT));
+
+ // Reconnect the device
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ type, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ address.c_str(), deviceName.c_str(), AUDIO_FORMAT_DEFAULT));
+
+ inputClientHandle += 1;
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, type, address, &devicePort));
+ routedPortId = devicePort.id;
+
+ // Reconnect the client changing voluntarily the io, but keeping the session to get the
+ // effect attached again
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &inputClientHandle, session, 1, &routedPortId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000));
+
+ // unregister effect should succeed since effect shall have been restore on the client session
+ ASSERT_EQ(NO_ERROR, mManager->unregisterEffect(effectId));
+}
\ No newline at end of file
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index 7ab9519..9e092c6 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -60,6 +60,11 @@
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
+ <mixPort name="mixport_bus_input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
</mixPorts>
<devicePorts>
<devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
@@ -84,6 +89,8 @@
</devicePort>
<devicePort tagName="USB Device In" type="AUDIO_DEVICE_IN_USB_DEVICE" role="source">
</devicePort>
+ <devicePort tagName="BUS Device In" type="AUDIO_DEVICE_IN_BUS" role="source" address="BUS00_MIC">
+ </devicePort>
</devicePorts>
<routes>
<route type="mix" sink="Speaker"
@@ -102,6 +109,8 @@
sources="primary output,hifi_output"/>
<route type="mix" sink="USB Device Out"
sources="primary output,hifi_output,mmap_no_irq_out"/>
+ <route type="mix" sink="mixport_bus_input"
+ sources="BUS Device In"/>
</routes>
</module>