audio: Simplify and extend alsa::Mixer

Remove alsa::MixerControl. tinyALSA contains utility
functions for setting values in percents, they use
the same logic as used to be there for handling
the "volume" control. Use access serialization at
the mixer level, rather than for each control.

Move the call to 'mixer_open' to alsa::Mixer.

Add controls for capture (mic) mute and gain. They
will be used by the primary HAL.

Bug: 264712385
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I0fad994153de96aceec3eb8f2fec19805ec912f8
diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp
index f0393e3..126c033 100644
--- a/audio/aidl/default/alsa/Mixer.cpp
+++ b/audio/aidl/default/alsa/Mixer.cpp
@@ -14,44 +14,17 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AHAL_AlsaMixer"
-#include <android-base/logging.h>
-
+#include <algorithm>
 #include <cmath>
 
+#define LOG_TAG "AHAL_AlsaMixer"
+#include <android-base/logging.h>
 #include <android/binder_status.h>
 
 #include "Mixer.h"
 
 namespace aidl::android::hardware::audio::core::alsa {
 
-//-----------------------------------------------------------------------------
-
-MixerControl::MixerControl(struct mixer_ctl* ctl)
-    : mCtl(ctl),
-      mNumValues(mixer_ctl_get_num_values(ctl)),
-      mMinValue(mixer_ctl_get_range_min(ctl)),
-      mMaxValue(mixer_ctl_get_range_max(ctl)) {}
-
-unsigned int MixerControl::getNumValues() const {
-    return mNumValues;
-}
-
-int MixerControl::getMaxValue() const {
-    return mMaxValue;
-}
-
-int MixerControl::getMinValue() const {
-    return mMinValue;
-}
-
-int MixerControl::setArray(const void* array, size_t count) {
-    const std::lock_guard guard(mLock);
-    return mixer_ctl_set_array(mCtl, array, count);
-}
-
-//-----------------------------------------------------------------------------
-
 // static
 const std::map<Mixer::Control, std::vector<Mixer::ControlNamesAndExpectedCtlType>>
         Mixer::kPossibleControls = {
@@ -60,18 +33,20 @@
                 {Mixer::HW_VOLUME,
                  {{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
                   {"Headset Playback Volume", MIXER_CTL_TYPE_INT},
-                  {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
+                  {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}},
+                {Mixer::MIC_SWITCH, {{"Capture Switch", MIXER_CTL_TYPE_BOOL}}},
+                {Mixer::MIC_GAIN, {{"Capture Volume", MIXER_CTL_TYPE_INT}}}};
 
 // static
-std::map<Mixer::Control, std::shared_ptr<MixerControl>> Mixer::initializeMixerControls(
-        struct mixer* mixer) {
-    std::map<Mixer::Control, std::shared_ptr<MixerControl>> mixerControls;
+Mixer::Controls Mixer::initializeMixerControls(struct mixer* mixer) {
+    if (mixer == nullptr) return {};
+    Controls mixerControls;
     std::string mixerCtlNames;
     for (const auto& [control, possibleCtls] : kPossibleControls) {
         for (const auto& [ctlName, expectedCtlType] : possibleCtls) {
             struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str());
             if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) {
-                mixerControls.emplace(control, std::make_unique<MixerControl>(ctl));
+                mixerControls.emplace(control, ctl);
                 if (!mixerCtlNames.empty()) {
                     mixerCtlNames += ",";
                 }
@@ -84,71 +59,141 @@
     return mixerControls;
 }
 
-Mixer::Mixer(struct mixer* mixer)
-    : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
+std::ostream& operator<<(std::ostream& s, Mixer::Control c) {
+    switch (c) {
+        case Mixer::Control::MASTER_SWITCH:
+            s << "master mute";
+            break;
+        case Mixer::Control::MASTER_VOLUME:
+            s << "master volume";
+            break;
+        case Mixer::Control::HW_VOLUME:
+            s << "volume";
+            break;
+        case Mixer::Control::MIC_SWITCH:
+            s << "mic mute";
+            break;
+        case Mixer::Control::MIC_GAIN:
+            s << "mic gain";
+            break;
+    }
+    return s;
+}
+
+Mixer::Mixer(int card) : mMixer(mixer_open(card)), mMixerControls(initializeMixerControls(mMixer)) {
+    if (!isValid()) {
+        PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
+    }
+}
 
 Mixer::~Mixer() {
-    mixer_close(mMixer);
+    if (isValid()) {
+        std::lock_guard l(mMixerAccess);
+        mixer_close(mMixer);
+    }
 }
 
-namespace {
-
-int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
-    return minValue + std::ceil((maxValue - minValue) * fValue);
-}
-
-}  // namespace
-
 ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
-    auto it = mMixerControls.find(Mixer::MASTER_SWITCH);
-    if (it == mMixerControls.end()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    }
-    const int numValues = it->second->getNumValues();
-    std::vector<int> values(numValues, muted ? 0 : 1);
-    if (int err = it->second->setArray(values.data(), numValues); err != 0) {
-        LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    return ndk::ScopedAStatus::ok();
+    return setMixerControlMute(MASTER_SWITCH, muted);
 }
 
 ndk::ScopedAStatus Mixer::setMasterVolume(float volume) {
-    auto it = mMixerControls.find(Mixer::MASTER_VOLUME);
-    if (it == mMixerControls.end()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    }
-    const int numValues = it->second->getNumValues();
-    std::vector<int> values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(),
-                                                            it->second->getMinValue()));
-    if (int err = it->second->setArray(values.data(), numValues); err != 0) {
-        LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    return ndk::ScopedAStatus::ok();
+    return setMixerControlVolume(MASTER_VOLUME, volume);
+}
+
+ndk::ScopedAStatus Mixer::setMicGain(float gain) {
+    return setMixerControlVolume(MIC_GAIN, gain);
+}
+
+ndk::ScopedAStatus Mixer::setMicMute(bool muted) {
+    return setMixerControlMute(MIC_SWITCH, muted);
 }
 
 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);
     }
-    const int numValues = it->second->getNumValues();
-    if (numValues < 0) {
-        LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
-    }
-    const int maxValue = it->second->getMaxValue();
-    const int minValue = it->second->getMinValue();
-    std::vector<int> values;
-    size_t i = 0;
-    for (; i < static_cast<size_t>(numValues) && i < values.size(); ++i) {
-        values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue));
-    }
-    if (int err = it->second->setArray(values.data(), values.size()); err != 0) {
+    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) {
         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) {
+    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);
+    }
+    std::lock_guard l(mMixerAccess);
+    if (int err = setMixerControlValue(it->second, muted ? 0 : 1); err != 0) {
+        LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+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);
+    }
+    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) {
+        LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << volume << ", err=" << err;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+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 ret;
+}
+
+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 ret;
+}
+
+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 ret;
+}
+
 }  // namespace aidl::android::hardware::audio::core::alsa