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

Commit 47d7e261 authored by Mikhail Naganov's avatar Mikhail Naganov Committed by Cherrypicker Worker
Browse files

libaudiohal@aidl: Align position reporting with HIDL

Update comments in StreamHalInterface to align with HIDL HAL
framework contract. Update StreamHalAidl to match the contract.

Bug: 338557486
Test: atest CtsMediaAudioTestCases
(cherry picked from https://android-review.googlesource.com/q/commit:f0a9fbc32ce1a89d987e18580b28585c0b2f4bac)
Merged-In: I7c21b93c1cc985b7176d2e7ec0207ee0f9abbdb1
Change-Id: I7c21b93c1cc985b7176d2e7ec0207ee0f9abbdb1
parent f450ced7
Loading
Loading
Loading
Loading
+57 −12
Original line number Diff line number Diff line
@@ -285,11 +285,12 @@ status_t StreamHalAidl::getLatency(uint32_t *latency) {
    return OK;
}

status_t StreamHalAidl::getObservablePosition(int64_t *frames, int64_t *timestamp) {
status_t StreamHalAidl::getObservablePosition(int64_t* frames, int64_t* timestamp,
        StatePositions* statePositions) {
    ALOGV("%p %s::%s", this, getClassName().c_str(), __func__);
    if (!mStream) return NO_INIT;
    StreamDescriptor::Reply reply;
    RETURN_STATUS_IF_ERROR(updateCountersIfNeeded(&reply));
    RETURN_STATUS_IF_ERROR(updateCountersIfNeeded(&reply, statePositions));
    *frames = std::max<int64_t>(0, reply.observable.frames);
    *timestamp = std::max<int64_t>(0, reply.observable.timeNs);
    return OK;
@@ -437,8 +438,12 @@ void StreamHalAidl::onAsyncDrainReady() {
    if (auto state = getState(); state == StreamDescriptor::State::DRAINING) {
        // Retrieve the current state together with position counters unconditionally
        // to ensure that the state on our side gets updated.
        sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(),
                nullptr, true /*safeFromNonWorkerThread */);
        sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(), nullptr,
                    true /*safeFromNonWorkerThread */);
        // For compatibility with HIDL behavior, apply a "soft" position reset
        // after receiving the "drain ready" callback.
        std::lock_guard l(mLock);
        mStatePositions.framesAtFlushOrDrain = mLastReply.observable.frames;
    } else {
        ALOGW("%s: unexpected onDrainReady in the state %s", __func__, toString(state).c_str());
    }
@@ -507,7 +512,7 @@ status_t StreamHalAidl::legacyReleaseAudioPatch() {
status_t StreamHalAidl::sendCommand(
        const ::aidl::android::hardware::audio::core::StreamDescriptor::Command& command,
        ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply,
        bool safeFromNonWorkerThread) {
        bool safeFromNonWorkerThread, StatePositions* statePositions) {
    // TIME_CHECK();  // TODO(b/243839867) reenable only when optimized.
    if (!safeFromNonWorkerThread) {
        const pid_t workerTid = mWorkerTid.load(std::memory_order_acquire);
@@ -539,6 +544,23 @@ status_t StreamHalAidl::sendCommand(
            }
            mLastReply = *reply;
            mLastReplyExpirationNs = uptimeNanos() + mLastReplyLifeTimeNs;
            if (!mIsInput && reply->status == STATUS_OK) {
                if (command.getTag() == StreamDescriptor::Command::standby &&
                        reply->state == StreamDescriptor::State::STANDBY) {
                    mStatePositions.framesAtStandby = reply->observable.frames;
                } else if (command.getTag() == StreamDescriptor::Command::flush &&
                           reply->state == StreamDescriptor::State::IDLE) {
                    mStatePositions.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;
                } // for asynchronous drain, the frame count is saved in 'onAsyncDrainReady'
            }
            if (statePositions != nullptr) {
                *statePositions = mStatePositions;
            }
        }
    }
    switch (reply->status) {
@@ -554,7 +576,8 @@ status_t StreamHalAidl::sendCommand(
}

status_t StreamHalAidl::updateCountersIfNeeded(
        ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply) {
        ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply,
        StatePositions* statePositions) {
    bool doUpdate = false;
    {
        std::lock_guard l(mLock);
@@ -564,10 +587,13 @@ status_t StreamHalAidl::updateCountersIfNeeded(
        // 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 */);
                reply, true /*safeFromNonWorkerThread */, statePositions);
    } else if (reply != nullptr) {  // provide cached reply
        std::lock_guard l(mLock);
        *reply = mLastReply;
        if (statePositions != nullptr) {
            *statePositions = mStatePositions;
        }
    }
    return OK;
}
@@ -659,8 +685,19 @@ status_t StreamOutHalAidl::getRenderPosition(uint64_t *dspFrames) {
        return BAD_VALUE;
    }
    int64_t aidlFrames = 0, aidlTimestamp = 0;
    RETURN_STATUS_IF_ERROR(getObservablePosition(&aidlFrames, &aidlTimestamp));
    *dspFrames = aidlFrames;
    StatePositions statePositions{};
    RETURN_STATUS_IF_ERROR(
            getObservablePosition(&aidlFrames, &aidlTimestamp, &statePositions));
    // Number of audio frames since the stream has exited standby.
    // 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;
    } else {
        mostRecentResetPoint =
                std::max(statePositions.framesAtStandby, statePositions.framesAtFlushOrDrain);
    }
    *dspFrames = aidlFrames <= mostRecentResetPoint ? 0 : aidlFrames - mostRecentResetPoint;
    return OK;
}

@@ -717,8 +754,16 @@ status_t StreamOutHalAidl::getPresentationPosition(uint64_t *frames, struct time
        return BAD_VALUE;
    }
    int64_t aidlFrames = 0, aidlTimestamp = 0;
    RETURN_STATUS_IF_ERROR(getObservablePosition(&aidlFrames, &aidlTimestamp));
    StatePositions statePositions{};
    RETURN_STATUS_IF_ERROR(getObservablePosition(&aidlFrames, &aidlTimestamp, &statePositions));
    // See the table at the start of 'StreamHalInterface'.
    if (!mContext.isAsynchronous() && audio_has_proportional_frames(mConfig.format)) {
        *frames = aidlFrames;
    } else {
        const int64_t mostRecentResetPoint =
                std::max(statePositions.framesAtStandby, statePositions.framesAtFlushOrDrain);
        *frames = aidlFrames <= mostRecentResetPoint ? 0 : aidlFrames - mostRecentResetPoint;
    }
    timestamp->tv_sec = aidlTimestamp / NANOS_PER_SECOND;
    timestamp->tv_nsec = aidlTimestamp - timestamp->tv_sec * NANOS_PER_SECOND;
    return OK;
+21 −5
Original line number Diff line number Diff line
@@ -194,6 +194,11 @@ class StreamHalAidl : public virtual StreamHalInterface, public ConversionHelper
    // For tests.
    friend class sp<StreamHalAidl>;

    struct StatePositions {
        int64_t framesAtFlushOrDrain;
        int64_t framesAtStandby;
    };

    template<class T>
    static std::shared_ptr<::aidl::android::hardware::audio::core::IStreamCommon> getStreamCommon(
            const std::shared_ptr<T>& stream);
@@ -212,7 +217,8 @@ class StreamHalAidl : public virtual StreamHalInterface, public ConversionHelper
    status_t getLatency(uint32_t *latency);

    // Always returns non-negative values.
    status_t getObservablePosition(int64_t *frames, int64_t *timestamp);
    status_t getObservablePosition(int64_t* frames, int64_t* timestamp,
            StatePositions* statePositions = nullptr);

    // Always returns non-negative values.
    status_t getHardwarePosition(int64_t *frames, int64_t *timestamp);
@@ -270,9 +276,11 @@ class StreamHalAidl : public virtual StreamHalInterface, public ConversionHelper
    status_t sendCommand(
            const ::aidl::android::hardware::audio::core::StreamDescriptor::Command& command,
            ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr,
            bool safeFromNonWorkerThread = false);
            bool safeFromNonWorkerThread = false,
            StatePositions* statePositions = nullptr);
    status_t updateCountersIfNeeded(
            ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr);
            ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr,
            StatePositions* statePositions = nullptr);

    const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamCommon> mStream;
    const std::shared_ptr<::aidl::android::media::audio::IHalAdapterVendorExtension> mVendorExt;
@@ -280,6 +288,9 @@ class StreamHalAidl : public virtual StreamHalInterface, public ConversionHelper
    std::mutex mLock;
    ::aidl::android::hardware::audio::core::StreamDescriptor::Reply mLastReply GUARDED_BY(mLock);
    int64_t mLastReplyExpirationNs GUARDED_BY(mLock) = 0;
    // Cached values of observable positions when the stream last entered certain state.
    // Updated for output streams only.
    StatePositions mStatePositions GUARDED_BY(mLock) = {};
    // mStreamPowerLog is used for audio signal power logging.
    StreamPowerLog mStreamPowerLog;
    std::atomic<pid_t> mWorkerTid = -1;
@@ -328,10 +339,14 @@ class StreamOutHalAidl : public virtual StreamOutHalInterface,
    // Requests notification when data buffered by the driver/hardware has been played.
    status_t drain(bool earlyNotify) override;

    // Notifies to the audio driver to flush the queued data.
    // Notifies to the audio driver to flush (that is, drop) the queued data. Stream must
    // already be paused before calling 'flush'.
    status_t flush() override;

    // Return a recent count of the number of audio frames presented to an external observer.
    // This excludes frames which have been written but are still in the pipeline. See the
    // table at the start of the 'StreamOutHalInterface' for the specification of the frame
    // count behavior w.r.t. 'flush', 'drain' and 'standby' operations.
    status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp) override;

    // Notifies the HAL layer that the framework considers the current playback as completed.
@@ -413,6 +428,7 @@ class StreamInHalAidl : public StreamInHalInterface, public StreamHalAidl {

    // Return a recent count of the number of audio frames received and
    // the clock time associated with that frame count.
    // The count must not reset to zero when a PCM input enters standby.
    status_t getCapturePosition(int64_t *frames, int64_t *time) override;

    // Get active microphones
+6 −1
Original line number Diff line number Diff line
@@ -161,10 +161,14 @@ class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl {
    // Requests notification when data buffered by the driver/hardware has been played.
    virtual status_t drain(bool earlyNotify);

    // Notifies to the audio driver to flush the queued data.
    // Notifies to the audio driver to flush (that is, drop) the queued data. Stream must
    // already be paused before calling 'flush'.
    virtual status_t flush();

    // Return a recent count of the number of audio frames presented to an external observer.
    // This excludes frames which have been written but are still in the pipeline. See the
    // table at the start of the 'StreamOutHalInterface' for the specification of the frame
    // count behavior w.r.t. 'flush', 'drain' and 'standby' operations.
    virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp);

    // Notifies the HAL layer that the framework considers the current playback as completed.
@@ -259,6 +263,7 @@ class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl {

    // Return a recent count of the number of audio frames received and
    // the clock time associated with that frame count.
    // The count must not reset to zero when a PCM input enters standby.
    virtual status_t getCapturePosition(int64_t *frames, int64_t *time);

    // Get active microphones
+38 −1
Original line number Diff line number Diff line
@@ -135,6 +135,38 @@ protected:
    virtual ~StreamOutHalInterfaceLatencyModeCallback() = default;
};

/**
 * On position reporting. There are two methods: 'getRenderPosition' and
 * 'getPresentationPosition'. The first difference is that they may have a
 * time offset because "render" position relates to what happens between
 * ADSP and DAC, while "observable" position is relative to the external
 * observer. The second difference is that 'getRenderPosition' always
 * resets on standby (for all types of stream data) according to its
 * definition. Since the original C definition of 'getRenderPosition' used
 * 32-bit frame counters, and also because in complex playback chains that
 * include wireless devices the "observable" position has more practical
 * meaning, 'getRenderPosition' does not exist in the AIDL HAL interface.
 * The table below summarizes frame count behavior for 'getPresentationPosition':
 *
 *               | Mixed      | Direct       | Direct
 *               |            | non-offload  | offload
 * ==============|============|==============|==============
 *  PCM and      | Continuous |              |
 *  encapsulated |            |              |
 *  bitstream    |            |              |
 * --------------|------------| Continuous†  |
 *  Bitstream    |            |              | Reset on
 *  encapsulated |            |              | flush, drain
 *  into PCM     |            |              | and standby
 *               | Not        |              |
 * --------------| supported  |--------------|
 *  Bitstream    |            | Reset on     |
 *               |            | flush, drain |
 *               |            | and standby  |
 *               |            |              |
 *
 * † - on standby, reset of the frame count happens at the framework level.
 */
class StreamOutHalInterface : public virtual StreamHalInterface {
  public:
    // Return the audio hardware driver estimated latency in milliseconds.
@@ -173,10 +205,14 @@ class StreamOutHalInterface : public virtual StreamHalInterface {
    // Requests notification when data buffered by the driver/hardware has been played.
    virtual status_t drain(bool earlyNotify) = 0;

    // Notifies to the audio driver to flush the queued data.
    // Notifies to the audio driver to flush (that is, drop) the queued data. Stream must
    // already be paused before calling 'flush'.
    virtual status_t flush() = 0;

    // Return a recent count of the number of audio frames presented to an external observer.
    // This excludes frames which have been written but are still in the pipeline. See the
    // table at the start of the 'StreamOutHalInterface' for the specification of the frame
    // count behavior w.r.t. 'flush', 'drain' and 'standby' operations.
    virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp) = 0;

    // Notifies the HAL layer that the framework considers the current playback as completed.
@@ -270,6 +306,7 @@ class StreamInHalInterface : public virtual StreamHalInterface {

    // Return a recent count of the number of audio frames received and
    // the clock time associated with that frame count.
    // The count must not reset to zero when a PCM input enters standby.
    virtual status_t getCapturePosition(int64_t *frames, int64_t *time) = 0;

    // Get active microphones