AudioFlinger: Control volume using Port ID
This CL migrates the volume management within AudioFlinger
from stream type to port ID.
It gives full power to AudioPolicy to compute the list of port
(so MmapThreads/Tracks) on which volume control is required.
It prevents from overwritting MUSIC stream type which is
the default for volume groups without associated stream type.
Bug: 317212590
Test: build & play audio
Test: atest audiopolicy_tests
Flag: com.android.media.audioserver.portid_volume_management
Change-Id: I4c9e8bb45660c9ceffcc0f4029b0617f9795ab3c
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 20cd40c..f2b59b7 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -187,6 +187,7 @@
BINDER_METHOD_ENTRY(masterMute) \
BINDER_METHOD_ENTRY(setStreamVolume) \
BINDER_METHOD_ENTRY(setStreamMute) \
+BINDER_METHOD_ENTRY(setPortsVolume) \
BINDER_METHOD_ENTRY(setMode) \
BINDER_METHOD_ENTRY(setMicMute) \
BINDER_METHOD_ENTRY(getMicMute) \
@@ -617,6 +618,7 @@
std::vector<audio_io_handle_t> secondaryOutputs;
bool isSpatialized;
bool isBitPerfect;
+ float volume;
ret = AudioSystem::getOutputForAttr(&localAttr, &io,
actualSessionId,
&streamType, adjAttributionSource,
@@ -624,7 +626,8 @@
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
deviceId, &portId, &secondaryOutputs, &isSpatialized,
- &isBitPerfect);
+ &isBitPerfect,
+ &volume);
if (ret != NO_ERROR) {
config->sample_rate = fullConfig.sample_rate;
config->channel_mask = fullConfig.channel_mask;
@@ -1061,6 +1064,7 @@
std::vector<audio_io_handle_t> secondaryOutputs;
bool isSpatialized = false;
bool isBitPerfect = false;
+ float volume;
audio_io_handle_t effectThreadId = AUDIO_IO_HANDLE_NONE;
std::vector<int> effectIds;
@@ -1121,7 +1125,7 @@
lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType,
adjAttributionSource, &input.config, input.flags,
&output.selectedDeviceId, &portId, &secondaryOutputs,
- &isSpatialized, &isBitPerfect);
+ &isSpatialized, &isBitPerfect, &volume);
if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
@@ -1178,7 +1182,7 @@
if (effectThread == nullptr) {
effectChain = getOrphanEffectChain_l(sessionId);
}
- ALOGV("createTrack() sessionId: %d", sessionId);
+ ALOGV("createTrack() sessionId: %d volume: %f", sessionId, volume);
output.sampleRate = input.config.sample_rate;
output.frameCount = input.frameCount;
@@ -1193,7 +1197,7 @@
input.sharedBuffer, sessionId, &output.flags,
callingPid, adjAttributionSource, input.clientInfo.clientTid,
&lStatus, portId, input.audioTrackCallback, isSpatialized,
- isBitPerfect, &output.afTrackFlags);
+ isBitPerfect, &output.afTrackFlags, volume);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
// we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
@@ -1644,6 +1648,37 @@
return NO_ERROR;
}
+status_t AudioFlinger::setPortsVolume(
+ const std::vector<audio_port_handle_t> &ports, float volume, audio_io_handle_t output)
+{
+ for (const auto& port : ports) {
+ if (port == AUDIO_PORT_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ }
+ if (isnan(volume) || volume > 1.0f || volume < 0.0f) {
+ return BAD_VALUE;
+ }
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ audio_utils::lock_guard lock(mutex());
+ for (const auto& port : ports) {
+ sp<VolumePortInterface> volumePortInterface = getVolumePortInterface_l(output, port);
+ if (volumePortInterface == nullptr) {
+ return BAD_VALUE;
+ }
+ volumePortInterface->setPortVolume(volume);
+ }
+ const sp<IAfMmapThread> mmapThread = checkMmapThread_l(output);
+ if (mmapThread) {
+ // send broadcast event only when all tracks volume is updated
+ audio_utils::lock_guard _l(mmapThread->mutex());
+ mmapThread->broadcast_l();
+ }
+ return NO_ERROR;
+}
+
status_t AudioFlinger::setRequestedLatencyMode(
audio_io_handle_t output, audio_latency_mode_t mode) {
if (output == AUDIO_IO_HANDLE_NONE) {
@@ -3824,8 +3859,7 @@
// checkPlaybackThread_l() must be called with AudioFlinger::mutex() held
-sp<VolumeInterface> AudioFlinger::getVolumeInterface_l(audio_io_handle_t output) const
-{
+sp<VolumeInterface> AudioFlinger::getVolumeInterface_l(audio_io_handle_t output) const {
sp<VolumeInterface> volumeInterface = mPlaybackThreads.valueFor(output).get();
if (volumeInterface == nullptr) {
IAfMmapThread* const mmapThread = mMmapThreads.valueFor(output).get();
@@ -3840,6 +3874,21 @@
return volumeInterface;
}
+sp<VolumePortInterface> AudioFlinger::getVolumePortInterface_l(audio_io_handle_t output,
+ audio_port_handle_t port) const
+{
+ IAfPlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread != nullptr) {
+ return thread->getVolumePortInterface(port);
+ }
+ const sp<IAfMmapThread> mmapThread = checkMmapThread_l(output);
+ if (mmapThread != nullptr && mmapThread->isOutput()) {
+ IAfMmapPlaybackThread *mmapPlaybackThread = mmapThread->asIAfMmapPlaybackThread().get();
+ return mmapPlaybackThread->getVolumePortInterface(port);
+ }
+ return nullptr;
+}
+
std::vector<sp<VolumeInterface>> AudioFlinger::getAllVolumeInterfaces_l() const
{
std::vector<sp<VolumeInterface>> volumeInterfaces;
@@ -5119,6 +5168,7 @@
case TransactionCode::GET_AUDIO_MIX_PORT:
case TransactionCode::SET_TRACKS_INTERNAL_MUTE:
case TransactionCode::RESET_REFERENCES_FOR_TEST:
+ case TransactionCode::SET_PORTS_VOLUME:
ALOGW("%s: transaction %d received from PID %d",
__func__, static_cast<int>(code), IPCThreadState::self()->getCallingPid());
// return status only for non void methods