audio: Implement getters for hardware mixer controls
The VTS test for volume accessors first determines whether
the accessor is supported by calling the getter. The getter
must return `UNSUPPORTED_OPERATION` if the control is not
supported. For this reason, wire the getter to the hardware
mixer implementation.
Bug: 302132812
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Ia50def0d076b6d3c46db55123186eab34bbbb954
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 6595324..e8c1693 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -848,7 +848,7 @@
}
StreamInHwGainHelper::StreamInHwGainHelper(const StreamContext* context)
- : mChannelCount(getChannelCount(context->getChannelLayout())) {}
+ : mChannelCount(getChannelCount(context->getChannelLayout())), mHwGains(mChannelCount, 0.0f) {}
ndk::ScopedAStatus StreamInHwGainHelper::getHwGainImpl(std::vector<float>* _aidl_return) {
*_aidl_return = mHwGains;
@@ -979,7 +979,8 @@
}
StreamOutHwVolumeHelper::StreamOutHwVolumeHelper(const StreamContext* context)
- : mChannelCount(getChannelCount(context->getChannelLayout())) {}
+ : mChannelCount(getChannelCount(context->getChannelLayout())),
+ mHwVolumes(mChannelCount, 0.0f) {}
ndk::ScopedAStatus StreamOutHwVolumeHelper::getHwVolumeImpl(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;
diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp
index 126c033..e72502b 100644
--- a/audio/aidl/default/alsa/Mixer.cpp
+++ b/audio/aidl/default/alsa/Mixer.cpp
@@ -20,9 +20,24 @@
#define LOG_TAG "AHAL_AlsaMixer"
#include <android-base/logging.h>
#include <android/binder_status.h>
+#include <error/expected_utils.h>
#include "Mixer.h"
+namespace ndk {
+
+// This enables use of 'error/expected_utils' for ScopedAStatus.
+
+inline bool errorIsOk(const ScopedAStatus& s) {
+ return s.isOk();
+}
+
+inline std::string errorToString(const ScopedAStatus& s) {
+ return s.getDescription();
+}
+
+} // namespace ndk
+
namespace aidl::android::hardware::audio::core::alsa {
// static
@@ -93,6 +108,36 @@
}
}
+ndk::ScopedAStatus Mixer::getMasterMute(bool* muted) {
+ return getMixerControlMute(MASTER_SWITCH, muted);
+}
+
+ndk::ScopedAStatus Mixer::getMasterVolume(float* volume) {
+ return getMixerControlVolume(MASTER_VOLUME, volume);
+}
+
+ndk::ScopedAStatus Mixer::getMicGain(float* gain) {
+ return getMixerControlVolume(MIC_GAIN, gain);
+}
+
+ndk::ScopedAStatus Mixer::getMicMute(bool* muted) {
+ return getMixerControlMute(MIC_SWITCH, muted);
+}
+
+ndk::ScopedAStatus Mixer::getVolumes(std::vector<float>* volumes) {
+ struct mixer_ctl* mctl;
+ RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl));
+ std::vector<int> percents;
+ std::lock_guard l(mMixerAccess);
+ if (int err = getMixerControlPercent(mctl, &percents); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to get volume, err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ std::transform(percents.begin(), percents.end(), std::back_inserter(*volumes),
+ [](int percent) -> float { return std::clamp(percent / 100.0f, 0.0f, 1.0f); });
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
return setMixerControlMute(MASTER_SWITCH, muted);
}
@@ -110,35 +155,70 @@
}
ndk::ScopedAStatus Mixer::setVolumes(const std::vector<float>& volumes) {
- if (!isValid()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- auto it = mMixerControls.find(Mixer::HW_VOLUME);
- if (it == mMixerControls.end()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
- }
+ struct mixer_ctl* mctl;
+ RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl));
std::vector<int> percents;
std::transform(
volumes.begin(), volumes.end(), std::back_inserter(percents),
[](float volume) -> int { return std::floor(std::clamp(volume, 0.0f, 1.0f) * 100); });
std::lock_guard l(mMixerAccess);
- if (int err = setMixerControlPercent(it->second, percents); err != 0) {
+ if (int err = setMixerControlPercent(mctl, percents); err != 0) {
LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Mixer::setMixerControlMute(Mixer::Control ctl, bool muted) {
+ndk::ScopedAStatus Mixer::findControl(Control ctl, struct mixer_ctl** result) {
if (!isValid()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- auto it = mMixerControls.find(ctl);
- if (it == mMixerControls.end()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ if (auto it = mMixerControls.find(ctl); it != mMixerControls.end()) {
+ *result = it->second;
+ return ndk::ScopedAStatus::ok();
}
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Mixer::getMixerControlMute(Control ctl, bool* muted) {
+ struct mixer_ctl* mctl;
+ RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
std::lock_guard l(mMixerAccess);
- if (int err = setMixerControlValue(it->second, muted ? 0 : 1); err != 0) {
+ std::vector<int> mutedValues;
+ if (int err = getMixerControlValues(mctl, &mutedValues); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (mutedValues.empty()) {
+ LOG(ERROR) << __func__ << ": got no values for " << ctl;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ *muted = mutedValues[0] != 0;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::getMixerControlVolume(Control ctl, float* volume) {
+ struct mixer_ctl* mctl;
+ RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
+ std::lock_guard l(mMixerAccess);
+ std::vector<int> percents;
+ if (int err = getMixerControlPercent(mctl, &percents); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (percents.empty()) {
+ LOG(ERROR) << __func__ << ": got no values for " << ctl;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ *volume = std::clamp(percents[0] / 100.0f, 0.0f, 1.0f);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setMixerControlMute(Control ctl, bool muted) {
+ struct mixer_ctl* mctl;
+ RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
+ std::lock_guard l(mMixerAccess);
+ if (int err = setMixerControlValue(mctl, muted ? 0 : 1); err != 0) {
LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
@@ -146,54 +226,72 @@
}
ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) {
- if (!isValid()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- auto it = mMixerControls.find(ctl);
- if (it == mMixerControls.end()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
- }
+ struct mixer_ctl* mctl;
+ RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
volume = std::clamp(volume, 0.0f, 1.0f);
std::lock_guard l(mMixerAccess);
- if (int err = setMixerControlPercent(it->second, std::floor(volume * 100)); err != 0) {
+ if (int err = setMixerControlPercent(mctl, std::floor(volume * 100)); err != 0) {
LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << volume << ", err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
+int Mixer::getMixerControlPercent(struct mixer_ctl* ctl, std::vector<int>* percents) {
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ percents->resize(n);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int valueOrError = mixer_ctl_get_percent(ctl, id); valueOrError >= 0) {
+ (*percents)[id] = valueOrError;
+ } else {
+ return valueOrError;
+ }
+ }
+ return 0;
+}
+
+int Mixer::getMixerControlValues(struct mixer_ctl* ctl, std::vector<int>* values) {
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ values->resize(n);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int valueOrError = mixer_ctl_get_value(ctl, id); valueOrError >= 0) {
+ (*values)[id] = valueOrError;
+ } else {
+ return valueOrError;
+ }
+ }
+ return 0;
+}
+
int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, int percent) {
- int ret = 0;
const unsigned int n = mixer_ctl_get_num_values(ctl);
for (unsigned int id = 0; id < n; id++) {
if (int error = mixer_ctl_set_percent(ctl, id, percent); error != 0) {
- ret = error;
+ return error;
}
}
- return ret;
+ return 0;
}
int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents) {
- int ret = 0;
const unsigned int n = mixer_ctl_get_num_values(ctl);
for (unsigned int id = 0; id < n; id++) {
if (int error = mixer_ctl_set_percent(ctl, id, id < percents.size() ? percents[id] : 0);
error != 0) {
- ret = error;
+ return error;
}
}
- return ret;
+ return 0;
}
int Mixer::setMixerControlValue(struct mixer_ctl* ctl, int value) {
- int ret = 0;
const unsigned int n = mixer_ctl_get_num_values(ctl);
for (unsigned int id = 0; id < n; id++) {
if (int error = mixer_ctl_set_value(ctl, id, value); error != 0) {
- ret = error;
+ return error;
}
}
- return ret;
+ return 0;
}
} // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/Mixer.h b/audio/aidl/default/alsa/Mixer.h
index 8fba1e0..41f19a8 100644
--- a/audio/aidl/default/alsa/Mixer.h
+++ b/audio/aidl/default/alsa/Mixer.h
@@ -39,6 +39,11 @@
bool isValid() const { return mMixer != nullptr; }
+ ndk::ScopedAStatus getMasterMute(bool* muted);
+ ndk::ScopedAStatus getMasterVolume(float* volume);
+ ndk::ScopedAStatus getMicGain(float* gain);
+ ndk::ScopedAStatus getMicMute(bool* muted);
+ ndk::ScopedAStatus getVolumes(std::vector<float>* volumes);
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
ndk::ScopedAStatus setMicGain(float gain);
@@ -60,9 +65,16 @@
static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
static Controls initializeMixerControls(struct mixer* mixer);
+ ndk::ScopedAStatus findControl(Control ctl, struct mixer_ctl** result);
+ ndk::ScopedAStatus getMixerControlMute(Control ctl, bool* muted);
+ ndk::ScopedAStatus getMixerControlVolume(Control ctl, float* volume);
ndk::ScopedAStatus setMixerControlMute(Control ctl, bool muted);
ndk::ScopedAStatus setMixerControlVolume(Control ctl, float volume);
+ int getMixerControlPercent(struct mixer_ctl* ctl, std::vector<int>* percents)
+ REQUIRES(mMixerAccess);
+ int getMixerControlValues(struct mixer_ctl* ctl, std::vector<int>* values)
+ REQUIRES(mMixerAccess);
int setMixerControlPercent(struct mixer_ctl* ctl, int percent) REQUIRES(mMixerAccess);
int setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents)
REQUIRES(mMixerAccess);
diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp
index 1b1ea68..7e3bdd4 100644
--- a/audio/aidl/default/primary/StreamPrimary.cpp
+++ b/audio/aidl/default/primary/StreamPrimary.cpp
@@ -127,6 +127,11 @@
if (isStubStream()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
+ float gain;
+ RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getMicGain(&gain));
+ _aidl_return->resize(0);
+ _aidl_return->resize(mChannelCount, gain);
+ RETURN_STATUS_IF_ERROR(setHwGainImpl(*_aidl_return));
return getHwGainImpl(_aidl_return);
}
@@ -194,6 +199,9 @@
if (isStubStream()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
+ RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(_aidl_return));
+ _aidl_return->resize(mChannelCount);
+ RETURN_STATUS_IF_ERROR(setHwVolumeImpl(*_aidl_return));
return getHwVolumeImpl(_aidl_return);
}