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

Commit 9555ad75 authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

libaudiohal@aidl: Provide HIDL-like 'createMmapBuffer'

If the AIDL HAL is at least V4 or it advertises MMAP
buffer re-creation support via the parameter
'aosp.createMmapBuffer' then use this call to implement
'StreamHalInterface::createMmapBuffer'. This allows
receiving updated file descriptor for the shared buffer.

Also, since AIDL HALs implementing 'createMmapBuffer'
(either way) will need to pass VTS tests which require
continuous non-retrograde hardware position reporting,
add necessary logic for offsetting the harware position
when reporting it to the framework, in order to stay
compatible with the HIDL behavior.

Another change is to use `burst` command for retrieving
the hardware position for MMAP streams that implement
'createMmapBuffer'. This is to fulfil the HAL API contract
which suggests that MMAP streams also use `burst` command
during audio I/O.

Bug: 274456992
Test: atest CoreAudioHalAidlTest
Test: atest CtsNativeMediaAAudioTestCases
Change-Id: I209c80b951758f8dd5f60e6df1992b29c010d118
parent ea785f09
Loading
Loading
Loading
Loading
+68 −22
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ using ::aidl::android::hardware::audio::core::IStreamIn;
using ::aidl::android::hardware::audio::core::IStreamOut;
using ::aidl::android::hardware::audio::core::MmapBufferDescriptor;
using ::aidl::android::hardware::audio::core::StreamDescriptor;
using ::aidl::android::hardware::audio::core::VendorParameter;
using ::aidl::android::media::audio::common::MicrophoneDynamicInfo;
using ::aidl::android::media::audio::IHalAdapterVendorExtension;

@@ -73,7 +74,15 @@ using ::aidl::android::media::audio::IHalAdapterVendorExtension;
namespace android {

using HalCommand = StreamDescriptor::Command;

namespace {

static constexpr int32_t kAidlVersion1 = 1;
static constexpr int32_t kAidlVersion2 = 2;
static constexpr int32_t kAidlVersion3 = 3;

static constexpr const char* kCreateMmapBuffer = "aosp.createMmapBuffer";

template<HalCommand::Tag cmd> HalCommand makeHalCommand() {
    return HalCommand::make<cmd>(::aidl::android::media::audio::common::Void{});
}
@@ -135,7 +144,8 @@ StreamHalAidl::StreamHalAidl(std::string_view className, bool isInput, const aud
        mStreamPowerLog.init(config.sample_rate, config.channel_mask, config.format);
    }

    if (mStream != nullptr) {
    if (mStream == nullptr) return;

    mContext.getCommandMQ()->setErrorHandler(
            fmqErrorHandler<StreamContextAidl::CommandMQ::Error>("CommandMQ"));
    mContext.getReplyMQ()->setErrorHandler(
@@ -144,6 +154,17 @@ StreamHalAidl::StreamHalAidl(std::string_view className, bool isInput, const aud
        mContext.getDataMQ()->setErrorHandler(
                fmqErrorHandler<StreamContextAidl::DataMQ::Error>("DataMQ"));
    }

    if (auto status = mStream->getInterfaceVersion(&mAidlInterfaceVersion); status.isOk()) {
        if (mAidlInterfaceVersion > kAidlVersion3) {
            mSupportsCreateMmapBuffer = true;
        } else {
            VendorParameter createMmapBuffer{.id = kCreateMmapBuffer};
            mSupportsCreateMmapBuffer =
                    mStream->setVendorParameters({createMmapBuffer}, false).isOk();
        }
    } else {
        AUGMENT_LOG(E, "failed to retrieve stream interface version: %s", status.getMessage());
    }
}

@@ -400,12 +421,17 @@ status_t StreamHalAidl::getHardwarePosition(int64_t *frames, int64_t *timestamp)
    AUGMENT_LOG(V);
    if (!mStream) return NO_INIT;
    StreamDescriptor::Reply reply;
    RETURN_STATUS_IF_ERROR(updateCountersIfNeeded(&reply));
    StatePositions statePositions{};
    RETURN_STATUS_IF_ERROR(updateCountersIfNeeded(&reply, &statePositions));
    if (reply.hardware.frames == StreamDescriptor::Position::UNKNOWN ||
        reply.hardware.timeNs == StreamDescriptor::Position::UNKNOWN) {
        AUGMENT_LOG(W, "No position was reported by the HAL");
        return INVALID_OPERATION;
    }
    *frames = reply.hardware.frames;
    int64_t mostRecentResetPoint = std::max(statePositions.hardware.framesAtStandby,
                                            statePositions.hardware.framesAtFlushOrDrain);
    int64_t aidlFrames = reply.hardware.frames;
    *frames = aidlFrames <= mostRecentResetPoint ? 0 : aidlFrames - mostRecentResetPoint;
    *timestamp = reply.hardware.timeNs;
    return OK;
}
@@ -627,7 +653,7 @@ void StreamHalAidl::onAsyncDrainReady() {
                                || mStatePositions.drainState == StatePositions::DrainState::ALL))) {
            AUGMENT_LOG(D, "setting position %lld as clip end",
                    (long long)mLastReply.observable.frames);
            mStatePositions.framesAtFlushOrDrain = mLastReply.observable.frames;
            mStatePositions.observable.framesAtFlushOrDrain = mLastReply.observable.frames;
        }
        mStatePositions.drainState = mStatePositions.drainState == StatePositions::DrainState::EN ?
                StatePositions::DrainState::EN_RECEIVED : StatePositions::DrainState::NONE;
@@ -650,12 +676,25 @@ status_t StreamHalAidl::createMmapBuffer(int32_t minSizeFrames __unused,
    if (!mContext.isMmapped()) {
        return BAD_VALUE;
    }
    if (mSupportsCreateMmapBuffer && (mAidlInterfaceVersion <= kAidlVersion3)) {
        std::vector<VendorParameter> parameters;
        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
                        mStream->getVendorParameters({kCreateMmapBuffer}, &parameters)));
        if (parameters.size() == 1) {
            std::optional<MmapBufferDescriptor> result;
            RETURN_STATUS_IF_ERROR(parameters[0].ext.getParcelable(&result));
            mContext.updateMmapBufferDescriptor(std::move(*result));
        } else {
            AUGMENT_LOG(E, "invalid output from 'createMmapBuffer' via 'getVendorParameters': %s",
                        internal::ToString(parameters).c_str());
            return INVALID_OPERATION;
        }
    }
    const MmapBufferDescriptor& bufferDescriptor = mContext.getMmapBufferDescriptor();
    info->shared_memory_fd = bufferDescriptor.sharedMemory.fd.get();
    info->buffer_size_frames = mContext.getBufferSizeFrames();
    info->burst_size_frames = bufferDescriptor.burstSizeFrames;
    info->flags = static_cast<audio_mmap_buffer_flag>(bufferDescriptor.flags);

    return OK;
}

@@ -727,15 +766,18 @@ status_t StreamHalAidl::sendCommand(
                if (reply->observable.frames != StreamDescriptor::Position::UNKNOWN) {
                    if (command.getTag() == StreamDescriptor::Command::standby &&
                            reply->state == StreamDescriptor::State::STANDBY) {
                        mStatePositions.framesAtStandby = reply->observable.frames;
                        mStatePositions.observable.framesAtStandby = reply->observable.frames;
                        mStatePositions.hardware.framesAtStandby = reply->hardware.frames;
                    } else if (command.getTag() == StreamDescriptor::Command::flush &&
                            reply->state == StreamDescriptor::State::IDLE) {
                        mStatePositions.framesAtFlushOrDrain = reply->observable.frames;
                        mStatePositions.observable.framesAtFlushOrDrain = reply->observable.frames;
                        mStatePositions.hardware.framesAtFlushOrDrain = reply->observable.frames;
                    } else if (!mContext.isAsynchronous() &&
                            command.getTag() == StreamDescriptor::Command::drain &&
                            (reply->state == StreamDescriptor::State::IDLE ||
                                    reply->state == StreamDescriptor::State::DRAINING)) {
                        mStatePositions.framesAtFlushOrDrain = reply->observable.frames;
                        mStatePositions.observable.framesAtFlushOrDrain = reply->observable.frames;
                        mStatePositions.hardware.framesAtFlushOrDrain = reply->observable.frames;
                    } // for asynchronous drain, the frame count is saved in 'onAsyncDrainReady'
                }
                if (mContext.isAsynchronous() &&
@@ -767,15 +809,19 @@ status_t StreamHalAidl::updateCountersIfNeeded(
        ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply,
        StatePositions* statePositions) {
    bool doUpdate = false;
    HalCommand cmd;
    {
        std::lock_guard l(mLock);
        doUpdate = uptimeNanos() > mLastReplyExpirationNs;
        cmd = mContext.isMmapped() && mSupportsCreateMmapBuffer
                && mLastReply.state == StreamDescriptor::State::ACTIVE
                ? makeHalCommand<HalCommand::Tag::burst>(0)
                : makeHalCommand<HalCommand::Tag::getStatus>();
    }
    if (doUpdate) {
        // Since updates are paced, it is OK to perform them from any thread, they should
        // not interfere with I/O operations of the worker.
        return sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(),
                reply, true /*safeFromNonWorkerThread */, statePositions);
        return sendCommand(cmd, reply, true /*safeFromNonWorkerThread */, statePositions);
    } else if (reply != nullptr) {  // provide cached reply
        std::lock_guard l(mLock);
        *reply = mLastReply;
@@ -882,10 +928,10 @@ status_t StreamOutHalAidl::getRenderPosition(uint64_t *dspFrames) {
    // See the table at the start of 'StreamHalInterface' on when it needs to reset.
    int64_t mostRecentResetPoint;
    if (!mContext.isAsynchronous() && audio_has_proportional_frames(mConfig.format)) {
        mostRecentResetPoint = statePositions.framesAtStandby;
        mostRecentResetPoint = statePositions.observable.framesAtStandby;
    } else {
        mostRecentResetPoint =
                std::max(statePositions.framesAtStandby, statePositions.framesAtFlushOrDrain);
        mostRecentResetPoint = std::max(statePositions.observable.framesAtStandby,
                statePositions.observable.framesAtFlushOrDrain);
    }
    *dspFrames = aidlFrames <= mostRecentResetPoint ? 0 : aidlFrames - mostRecentResetPoint;
    return OK;
@@ -961,8 +1007,8 @@ status_t StreamOutHalAidl::getPresentationPosition(uint64_t *frames, struct time
    if (!mContext.isAsynchronous() && audio_has_proportional_frames(mConfig.format)) {
        *frames = aidlFrames;
    } else {
        const int64_t mostRecentResetPoint =
                std::max(statePositions.framesAtStandby, statePositions.framesAtFlushOrDrain);
        const int64_t mostRecentResetPoint = std::max(statePositions.observable.framesAtStandby,
                statePositions.observable.framesAtFlushOrDrain);
        *frames = aidlFrames <= mostRecentResetPoint ? 0 : aidlFrames - mostRecentResetPoint;
    }
    timestamp->tv_sec = aidlTimestamp / NANOS_PER_SECOND;
+16 −9
Original line number Diff line number Diff line
@@ -37,9 +37,6 @@
#include "ConversionHelperAidl.h"
#include "StreamPowerLog.h"

using ::aidl::android::hardware::audio::common::AudioOffloadMetadata;
using ::aidl::android::hardware::audio::core::MmapBufferDescriptor;

namespace android {

class StreamContextAidl {
@@ -87,10 +84,14 @@ class StreamContextAidl {
    ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
    bool isAsynchronous() const { return mIsAsynchronous; }
    bool isMmapped() const { return mIsMmapped; }
    const MmapBufferDescriptor& getMmapBufferDescriptor() const { return mMmapBufferDescriptor; }
    const ::aidl::android::hardware::audio::core::MmapBufferDescriptor&
            getMmapBufferDescriptor() const { return mMmapBufferDescriptor; }
    size_t getMmapBurstSize() const { return mMmapBufferDescriptor.burstSizeFrames; }
    int getIoHandle() const { return mIoHandle; }
    bool hasClipTransitionSupport() const { return mHasClipTransitionSupport; }
    void updateMmapBufferDescriptor(
            ::aidl::android::hardware::audio::core::MmapBufferDescriptor&& desc) {
        mMmapBufferDescriptor = std::move(desc); }

  private:
    static std::unique_ptr<DataMQ> maybeCreateDataMQ(
@@ -106,7 +107,7 @@ class StreamContextAidl {
        using Tag = ::aidl::android::hardware::audio::core::StreamDescriptor::AudioBuffer::Tag;
        return descriptor.audio.getTag() == Tag::mmap;
    }
    static MmapBufferDescriptor maybeGetMmapBuffer(
    static ::aidl::android::hardware::audio::core::MmapBufferDescriptor maybeGetMmapBuffer(
            ::aidl::android::hardware::audio::core::StreamDescriptor& descriptor) {
        using Tag = ::aidl::android::hardware::audio::core::StreamDescriptor::AudioBuffer::Tag;
        if (descriptor.audio.getTag() == Tag::mmap) {
@@ -122,7 +123,7 @@ class StreamContextAidl {
    std::unique_ptr<DataMQ> mDataMQ;
    bool mIsAsynchronous;
    bool mIsMmapped;
    MmapBufferDescriptor mMmapBufferDescriptor;
    ::aidl::android::hardware::audio::core::MmapBufferDescriptor mMmapBufferDescriptor;
    int mIoHandle;
    bool mHasClipTransitionSupport;
};
@@ -183,9 +184,13 @@ class StreamHalAidl : public virtual StreamHalInterface, public ConversionHelper
    // For tests.
    friend class sp<StreamHalAidl>;

    struct StatePositions {
    struct FrameCounters {
        int64_t framesAtFlushOrDrain;
        int64_t framesAtStandby;
    };
    struct StatePositions {
        FrameCounters observable;
        FrameCounters hardware;
        enum DrainState : int32_t { NONE, ALL, EN /*early notify*/, EN_RECEIVED };
        DrainState drainState;
    };
@@ -288,7 +293,7 @@ class StreamHalAidl : public virtual StreamHalInterface, public ConversionHelper

    const bool mIsInput;
    const audio_config_base_t mConfig;
    const StreamContextAidl mContext;
    StreamContextAidl mContext;
    // This lock is used to make sending of a command and receiving a reply an atomic
    // operation. Otherwise, when two threads are trying to send a command, they may both advance to
    // reading of the reply once the HAL has consumed the command from the MQ, and that creates a
@@ -340,6 +345,8 @@ class StreamHalAidl : public virtual StreamHalInterface, public ConversionHelper
    // mStreamPowerLog is used for audio signal power logging.
    StreamPowerLog mStreamPowerLog;
    std::atomic<pid_t> mWorkerTid = -1;
    int32_t mAidlInterfaceVersion = -1;
    bool mSupportsCreateMmapBuffer = false;
};

class CallbackBroker;
@@ -446,7 +453,7 @@ class StreamOutHalAidl : public virtual StreamOutHalInterface,
    const wp<CallbackBroker> mCallbackBroker;
    mediautils::atomic_wp<StreamOutHalInterfaceCallback> mClientCallback;

    AudioOffloadMetadata mOffloadMetadata;
    ::aidl::android::hardware::audio::common::AudioOffloadMetadata mOffloadMetadata;

    // Can not be constructed directly by clients.
    StreamOutHalAidl(
+6 −0
Original line number Diff line number Diff line
@@ -61,6 +61,10 @@ using ::aidl::android::media::audio::common::PcmType;

class VendorParameterMock {
  public:
    void clearParameters() {
        mAsyncParameters.clear();
        mSyncParameters.clear();
    }
    const std::vector<std::string>& getRetrievedParameterIds() const { return mGetParameterIds; }
    const std::vector<VendorParameter>& getAsyncParameters() const { return mAsyncParameters; }
    const std::vector<VendorParameter>& getSyncParameters() const { return mSyncParameters; }
@@ -995,6 +999,8 @@ class StreamHalAidlVendorParametersTest : public testing::Test {
                                  false /*hasClipTransitionSupport*/);
        mStream = sp<StreamHalAidl>::make("test", false /*isInput*/, config, 0 /*nominalLatency*/,
                                          std::move(context), mStreamCommon, mVendorExt);
        // The stream may check for some properties after creating.
        mStreamCommon->clearParameters();
    }
    void TearDown() override {
        mStream.clear();
+1 −0
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ const char * const AudioParameter::keyOffloadCodecPaddingSamples =
        AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES;
const char * const AudioParameter::keyClipTransitionSupport =
        AUDIO_PARAMETER_CLIP_TRANSITION_SUPPORT;
const char * const keyCreateMmapBuffer = AUDIO_PARAMETER_CREATE_MMAP_BUFFER;

AudioParameter::AudioParameter(const String8& keyValuePairs)
{
+1 −0
Original line number Diff line number Diff line
@@ -152,6 +152,7 @@ public:
    static const char * const keyOffloadCodecPaddingSamples;

    static const char * const keyClipTransitionSupport;
    static const char * const keyCreateMmapBuffer;

    String8 toString() const { return toStringImpl(true); }
    String8 keysToString() const { return toStringImpl(false); }