Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3143e693 authored by Mikhail Naganov's avatar Mikhail Naganov Committed by Gerrit Code Review
Browse files

Merge changes Ic51d603d,Ia50def0d,I22f65b8b,I8ce9f230,Id8455eb1, ... into main

* changes:
  audio: Query minimum buffer size before opening streams
  audio: Implement getters for hardware mixer controls
  audio: Clean up and fix the bluetooth HAL module
  audio: Provide a way for Module to specify nominal latency
  audio: Fix default remote submix HAL implementation and VTS
  CSD: Add default AIDL HAL implementation
parents 6a4d7630 95f22777
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -174,4 +174,12 @@ constexpr U makeBitPositionFlagMask(std::initializer_list<E> flags) {
    return result;
}

constexpr int32_t frameCountFromDurationUs(long durationUs, int32_t sampleRateHz) {
    return (durationUs * sampleRateHz) / 1000000LL;
}

constexpr int32_t frameCountFromDurationMs(int32_t durationMs, int32_t sampleRateHz) {
    return frameCountFromDurationUs(durationMs * 1000, sampleRateHz);
}

}  // namespace aidl::android::hardware::audio::common
+55 −43
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@
#include <set>

#define LOG_TAG "AHAL_Module"
#include <Utils.h>
#include <aidl/android/media/audio/common/AudioInputFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include <android-base/logging.h>
@@ -35,6 +34,7 @@
#include "core-impl/SoundDose.h"
#include "core-impl/utils.h"

using aidl::android::hardware::audio::common::frameCountFromDurationMs;
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
using aidl::android::hardware::audio::common::isBitPositionFlagSet;
using aidl::android::hardware::audio::common::isValidAudioMode;
@@ -202,15 +202,17 @@ ndk::ScopedAStatus Module::createStreamContext(
        LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    if (in_bufferSizeFrames < kMinimumStreamBufferSizeFrames) {
        LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
                   << ", must be at least " << kMinimumStreamBufferSizeFrames;
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    auto& configs = getConfig().portConfigs;
    auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
    // Since this is a private method, it is assumed that
    // validity of the portConfigId has already been checked.
    const int32_t minimumStreamBufferSizeFrames = calculateBufferSizeFrames(
            getNominalLatencyMs(*portConfigIt), portConfigIt->sampleRate.value().value);
    if (in_bufferSizeFrames < minimumStreamBufferSizeFrames) {
        LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
                   << ", must be at least " << minimumStreamBufferSizeFrames;
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    const size_t frameSize =
            getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
    if (frameSize == 0) {
@@ -238,11 +240,12 @@ ndk::ScopedAStatus Module::createStreamContext(
        StreamContext temp(
                std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
                portConfigIt->portId, portConfigIt->format.value(),
                portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags,
                portConfigIt->format.value(), portConfigIt->channelMask.value(),
                portConfigIt->sampleRate.value().value, flags, getNominalLatencyMs(*portConfigIt),
                portConfigIt->ext.get<AudioPortExt::mix>().handle,
                std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
                asyncCallback, outEventCallback, params);
                asyncCallback, outEventCallback,
                std::weak_ptr<sounddose::StreamDataProcessorInterface>{}, params);
        if (temp.isValid()) {
            *out_context = std::move(temp);
        } else {
@@ -358,6 +361,12 @@ std::unique_ptr<Module::Configuration> Module::initializeConfig() {
    return internal::getConfiguration(getType());
}

int32_t Module::getNominalLatencyMs(const AudioPortConfig&) {
    // Arbitrary value. Implementations must override this method to provide their actual latency.
    static constexpr int32_t kLatencyMs = 5;
    return kLatencyMs;
}

std::vector<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
    std::vector<AudioRoute*> result;
    auto& routes = getConfig().routes;
@@ -608,10 +617,11 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA

    std::vector<AudioRoute*> routesToMixPorts = getAudioRoutesForAudioPortImpl(templateId);
    std::set<int32_t> routableMixPortIds = getRoutableAudioPortIds(templateId, &routesToMixPorts);
    if (hasDynamicProfilesOnly(connectedPort.profiles)) {
    if (!mDebug.simulateDeviceConnections) {
        // Even if the device port has static profiles, the HAL module might need to update
        // them, or abort the connection process.
        RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort));
        } else {
    } else if (hasDynamicProfilesOnly(connectedPort.profiles)) {
        auto& connectedProfiles = getConfig().connectedProfiles;
        if (auto connectedProfilesIt = connectedProfiles.find(templateId);
            connectedProfilesIt != connectedProfiles.end()) {
@@ -622,20 +632,17 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
        // Possible case 2. Check if all routable mix ports have static profiles.
        if (auto dynamicMixPortIt = std::find_if(ports.begin(), ports.end(),
                                                 [&routableMixPortIds](const auto& p) {
                                                         return routableMixPortIds.count(p.id) >
                                                                        0 &&
                                                     return routableMixPortIds.count(p.id) > 0 &&
                                                            hasDynamicProfilesOnly(p.profiles);
                                                 });
            dynamicMixPortIt != ports.end()) {
                LOG(ERROR) << __func__
                           << ": connected port only has dynamic profiles after connecting "
            LOG(ERROR) << __func__ << ": connected port only has dynamic profiles after connecting "
                       << "external device " << connectedPort.toString() << ", and there exist "
                       << "a routable mix port with dynamic profiles: "
                       << dynamicMixPortIt->toString();
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
        }
    }
    }

    connectedPort.id = getConfig().nextPortId++;
    auto [connectedPortsIt, _] =
@@ -964,11 +971,21 @@ ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPa
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
        }
    }
    // Find the highest sample rate among mix port configs.
    std::map<int32_t, AudioPortConfig*> sampleRates;
    std::vector<AudioPortConfig*>& mixPortConfigs =
            sources[0]->ext.getTag() == AudioPortExt::mix ? sources : sinks;
    for (auto mix : mixPortConfigs) {
        sampleRates.emplace(mix->sampleRate.value().value, mix);
    }
    *_aidl_return = in_requested;
    _aidl_return->minimumStreamBufferSizeFrames = kMinimumStreamBufferSizeFrames;
    auto maxSampleRateIt = std::max_element(sampleRates.begin(), sampleRates.end());
    const int32_t latencyMs = getNominalLatencyMs(*(maxSampleRateIt->second));
    _aidl_return->minimumStreamBufferSizeFrames =
            calculateBufferSizeFrames(latencyMs, maxSampleRateIt->first);
    _aidl_return->latenciesMs.clear();
    _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
                                     _aidl_return->sinkPortConfigIds.size(), kLatencyMs);
                                     _aidl_return->sinkPortConfigIds.size(), latencyMs);
    AudioPatch oldPatch{};
    if (existing == patches.end()) {
        _aidl_return->id = getConfig().nextPatchId++;
@@ -1210,7 +1227,7 @@ ndk::ScopedAStatus Module::setMasterMute(bool in_mute) {
        // Reset master mute if it failed.
        onMasterMuteChanged(mMasterMute);
    }
    return std::move(result);
    return result;
}

ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
@@ -1232,7 +1249,7 @@ ndk::ScopedAStatus Module::setMasterVolume(float in_volume) {
                       << "), error=" << result;
            onMasterVolumeChanged(mMasterVolume);
        }
        return std::move(result);
        return result;
    }
    LOG(ERROR) << __func__ << ": invalid master volume value: " << in_volume;
    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -1553,11 +1570,6 @@ std::vector<MicrophoneInfo> Module::getMicrophoneInfos() {
    return result;
}

Module::BtProfileHandles Module::getBtProfileManagerHandles() {
    return std::make_tuple(std::weak_ptr<IBluetooth>(), std::weak_ptr<IBluetoothA2dp>(),
                           std::weak_ptr<IBluetoothLe>());
}

ndk::ScopedAStatus Module::bluetoothParametersUpdated() {
    return mStreams.bluetoothParametersUpdated();
}
+8 −0
Original line number Diff line number Diff line
@@ -58,4 +58,12 @@ ndk::ScopedAStatus ModulePrimary::createOutputStream(
                                                  offloadInfo);
}

int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig&) {
    // 85 ms is chosen considering 4096 frames @ 48 kHz. This is the value which allows
    // the virtual Android device implementation to pass CTS. Hardware implementations
    // should have significantly lower latency.
    static constexpr int32_t kLatencyMs = 85;
    return kLatencyMs;
}

}  // namespace aidl::android::hardware::audio::core
+18 −5
Original line number Diff line number Diff line
@@ -90,6 +90,14 @@ bool StreamContext::isValid() const {
    return true;
}

void StreamContext::startStreamDataProcessor() {
    auto streamDataProcessor = mStreamDataProcessor.lock();
    if (streamDataProcessor != nullptr) {
        streamDataProcessor->startDataProcessor(mSampleRate, getChannelCount(mChannelLayout),
                                                mFormat);
    }
}

void StreamContext::reset() {
    mCommandMQ.reset();
    mReplyMQ.reset();
@@ -130,7 +138,7 @@ void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
    reply->status = STATUS_OK;
    if (isConnected) {
        reply->observable.frames = mContext->getFrameCount();
        reply->observable.timeNs = ::android::elapsedRealtimeNano();
        reply->observable.timeNs = ::android::uptimeNanos();
        if (auto status = mDriver->refinePosition(&reply->observable); status == ::android::OK) {
            return;
        }
@@ -307,7 +315,7 @@ bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply
    const size_t frameSize = mContext->getFrameSize();
    size_t actualFrameCount = 0;
    bool fatal = false;
    int32_t latency = Module::kLatencyMs;
    int32_t latency = mContext->getNominalLatencyMs();
    if (isConnected) {
        if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize,
                                                           &actualFrameCount, &latency);
@@ -573,7 +581,7 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep
    const size_t readByteCount = dataMQ->availableToRead();
    const size_t frameSize = mContext->getFrameSize();
    bool fatal = false;
    int32_t latency = Module::kLatencyMs;
    int32_t latency = mContext->getNominalLatencyMs();
    if (bool success = readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) {
        const bool isConnected = mIsConnected;
        LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
@@ -593,6 +601,10 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep
                fatal = true;
                LOG(ERROR) << __func__ << ": write failed: " << status;
            }
            auto streamDataProcessor = mContext->getStreamDataProcessor().lock();
            if (streamDataProcessor != nullptr) {
                streamDataProcessor->process(mDataBuffer.get(), actualFrameCount * frameSize);
            }
        } else {
            if (mContext->getAsyncCallback() == nullptr) {
                usleep(3000);  // Simulate blocking transfer delay.
@@ -836,7 +848,7 @@ ndk::ScopedAStatus StreamIn::setHwGain(const std::vector<float>& in_channelGains
}

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;
@@ -967,7 +979,8 @@ ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int3
}

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;
+127 −29
Original line number Diff line number Diff line
@@ -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 @@ Mixer::~Mixer() {
    }
}

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,90 +155,143 @@ ndk::ScopedAStatus Mixer::setMicMute(bool 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);
    }
    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()) {
    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) {
        LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err;
    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::setMixerControlVolume(Control ctl, float volume) {
    if (!isValid()) {
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);
    }
    auto it = mMixerControls.find(ctl);
    if (it == mMixerControls.end()) {
        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
    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);
    }
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) {
    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
Loading