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

Commit b249d271 authored by Atneya Nair's avatar Atneya Nair
Browse files

Refactor AudioRecord callback to interface

Refactor AudioRecord callback from static function pointer
with pointer args to callback interface.

 - Removes need for event enum, user cookie, and opaque arg
   ptr.
 - Replace callback_t with IAudioRecordCallback listener interface for
   separate notifications for each event type.
 - Duplicate set/constructor methods to provide for incremental
   refactor.
 - LegacyCallbackWrapper to wrap legacy_callback_t consuming methods
 - Retain wp<IAudioTrackCallback> to refcount and avoid ownership
   cycles.

Related-to: I67dc284307419737eeb055db851751455abf103d
Bug: 199156212
Test: Compiles. To be verified.
Change-Id: Id75c8866bdfa528d37cf1f93e41e917568c4a147
parent d24c4d23
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -128,7 +128,7 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder)
    uint32_t notificationFrames = 0;

    // Setup the callback if there is one.
    AudioRecord::callback_t callback = nullptr;
    AudioRecord::legacy_callback_t callback = nullptr;
    void *callbackData = nullptr;
    AudioRecord::transfer_type streamTransferType = AudioRecord::transfer_type::TRANSFER_SYNC;
    if (builder.getDataCallbackProc() != nullptr) {
+124 −27
Original line number Diff line number Diff line
@@ -143,7 +143,7 @@ AudioRecord::AudioRecord(
        audio_channel_mask_t channelMask,
        const AttributionSourceState& client,
        size_t frameCount,
        callback_t cbf,
        legacy_callback_t callback,
        void* user,
        uint32_t notificationFrames,
        audio_session_t sessionId,
@@ -163,7 +163,39 @@ AudioRecord::AudioRecord(
{
    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientAttributionSource.uid));
    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid));
    (void)set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
    (void)set(inputSource, sampleRate, format, channelMask, frameCount, callback, user,
            notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
            uid, pid, pAttributes, selectedDeviceId, selectedMicDirection,
            microphoneFieldDimension);
}

AudioRecord::AudioRecord(
        audio_source_t inputSource,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        const AttributionSourceState& client,
        size_t frameCount,
        const wp<IAudioRecordCallback>& callback,
        uint32_t notificationFrames,
        audio_session_t sessionId,
        transfer_type transferType,
        audio_input_flags_t flags,
        const audio_attributes_t* pAttributes,
        audio_port_handle_t selectedDeviceId,
        audio_microphone_direction_t selectedMicDirection,
        float microphoneFieldDimension)
    : mActive(false),
      mStatus(NO_INIT),
      mClientAttributionSource(client),
      mSessionId(AUDIO_SESSION_ALLOCATE),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mProxy(nullptr)
{
    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientAttributionSource.uid));
    pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid));
    (void)set(inputSource, sampleRate, format, channelMask, frameCount, callback,
            notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
            uid, pid, pAttributes, selectedDeviceId, selectedMicDirection,
            microphoneFieldDimension);
@@ -218,14 +250,44 @@ void AudioRecord::stopAndJoinCallbacks() {
        AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId);
    }
}
namespace {
class LegacyCallbackWrapper : public AudioRecord::IAudioRecordCallback {
    const AudioRecord::legacy_callback_t mCallback;
    void* const mData;

  public:
    LegacyCallbackWrapper(AudioRecord::legacy_callback_t callback, void* user)
        : mCallback(callback), mData(user) {}

    size_t onMoreData(const AudioRecord::Buffer& buffer) override {
        AudioRecord::Buffer copy = buffer;
        mCallback(AudioRecord::EVENT_MORE_DATA, mData, &copy);
        return copy.size;
    }

    void onOverrun() override { mCallback(AudioRecord::EVENT_OVERRUN, mData, nullptr); }

    void onMarker(uint32_t markerPosition) override {
        mCallback(AudioRecord::EVENT_MARKER, mData, &markerPosition);
    }

    void onNewPos(uint32_t newPos) override {
        mCallback(AudioRecord::EVENT_NEW_POS, mData, &newPos);
    }

    void onNewIAudioRecord() override {
        mCallback(AudioRecord::EVENT_NEW_IAUDIORECORD, mData, nullptr);
    }
};
}  // namespace

status_t AudioRecord::set(
        audio_source_t inputSource,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        size_t frameCount,
        callback_t cbf,
        void* user,
        const wp<IAudioRecordCallback>& callback,
        uint32_t notificationFrames,
        bool threadCanCallJava,
        audio_session_t sessionId,
@@ -241,7 +303,7 @@ status_t AudioRecord::set(
{
    status_t status = NO_ERROR;
    uint32_t channelCount;

    const sp<IAudioRecordCallback> callbackHandle = callback.promote();
    // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
    ALOGV("%s(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
          "notificationFrames %u, sessionId %d, transferType %d, flags %#x, attributionSource %s"
@@ -274,15 +336,15 @@ status_t AudioRecord::set(

    switch (transferType) {
    case TRANSFER_DEFAULT:
        if (cbf == NULL || threadCanCallJava) {
        if (callbackHandle == nullptr || threadCanCallJava) {
            transferType = TRANSFER_SYNC;
        } else {
            transferType = TRANSFER_CALLBACK;
        }
        break;
    case TRANSFER_CALLBACK:
        if (cbf == NULL) {
            ALOGE("%s(): Transfer type TRANSFER_CALLBACK but cbf == NULL", __func__);
        if (callbackHandle == nullptr) {
            ALOGE("%s(): Transfer type TRANSFER_CALLBACK but callback == nullptr", __func__);
            status = BAD_VALUE;
            goto exit;
        }
@@ -304,7 +366,7 @@ status_t AudioRecord::set(
        goto exit;
    }

    if (pAttributes == NULL) {
    if (pAttributes == nullptr) {
        mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
        mAttributes.source = inputSource;
        if (inputSource == AUDIO_SOURCE_VOICE_COMMUNICATION
@@ -360,9 +422,9 @@ status_t AudioRecord::set(
    ALOGV("%s(): mSessionId %d", __func__, mSessionId);

    mOrigFlags = mFlags = flags;
    mCbf = cbf;
    mCallback = callbackHandle;

    if (cbf != NULL) {
    if (mCallback != nullptr) {
        mAudioRecordThread = new AudioRecordThread(*this);
        mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
        // thread begins in paused state, and will not reference us until start()
@@ -385,7 +447,6 @@ status_t AudioRecord::set(
        goto exit;
    }

    mUserData = user;
    // TODO: add audio hardware input latency here
    mLatency = (1000LL * mFrameCount) / mSampleRate;
    mMarkerPosition = 0;
@@ -407,6 +468,37 @@ exit:
    return status;
}

status_t AudioRecord::set(
        audio_source_t inputSource,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        size_t frameCount,
        legacy_callback_t callback,
        void* user,
        uint32_t notificationFrames,
        bool threadCanCallJava,
        audio_session_t sessionId,
        transfer_type transferType,
        audio_input_flags_t flags,
        uid_t uid,
        pid_t pid,
        const audio_attributes_t* pAttributes,
        audio_port_handle_t selectedDeviceId,
        audio_microphone_direction_t selectedMicDirection,
        float microphoneFieldDimension,
        int32_t maxSharedAudioHistoryMs)
{
    if (callback != nullptr) {
        mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
    } else if (user) {
        LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
    }
    return set(inputSource, sampleRate, format, channelMask, frameCount, mLegacyCallbackWrapper,
        notificationFrames, threadCanCallJava, sessionId, transferType, flags, uid, pid,
        pAttributes, selectedDeviceId, selectedMicDirection, microphoneFieldDimension,
        maxSharedAudioHistoryMs);
}
// -------------------------------------------------------------------------

status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t triggerSession)
@@ -536,12 +628,12 @@ bool AudioRecord::stopped() const

status_t AudioRecord::setMarkerPosition(uint32_t marker)
{
    AutoMutex lock(mLock);
    // The only purpose of setting marker position is to get a callback
    if (mCbf == NULL) {
    if (mCallback.promote() == nullptr) {
        return INVALID_OPERATION;
    }

    AutoMutex lock(mLock);
    mMarkerPosition = marker;
    mMarkerReached = false;

@@ -566,12 +658,12 @@ status_t AudioRecord::getMarkerPosition(uint32_t *marker) const

status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
{
    AutoMutex lock(mLock);
    // The only purpose of setting position update period is to get a callback
    if (mCbf == NULL) {
    if (mCallback.promote() == nullptr) {
        return INVALID_OPERATION;
    }

    AutoMutex lock(mLock);
    mNewPosition = mProxy->getPosition() + updatePeriod;
    mUpdatePeriod = updatePeriod;

@@ -757,7 +849,7 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch)
    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
    IAudioFlinger::CreateRecordInput input;
    IAudioFlinger::CreateRecordOutput output;
    audio_session_t originalSessionId;
    [[maybe_unused]] audio_session_t originalSessionId;
    void *iMemPointer;
    audio_track_cblk_t* cblk;
    status_t status;
@@ -926,7 +1018,7 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch)
                mNotificationFramesReq, output.notificationFrameCount, output.frameCount);
    }
    mNotificationFramesAct = (uint32_t)output.notificationFrameCount;
    if (mServerConfig.format != mFormat && mCbf != nullptr) {
    if (mServerConfig.format != mFormat && mCallback.promote() != nullptr) {
        mFormatConversionBufRaw = std::make_unique<uint8_t[]>(mNotificationFramesAct * mFrameSize);
        mFormatConversionBuffer.raw = mFormatConversionBufRaw.get();
    }
@@ -1182,6 +1274,11 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize, bool blocking)
nsecs_t AudioRecord::processAudioBuffer()
{
    mLock.lock();
    const sp<IAudioRecordCallback> callback = mCallback.promote();
    if (!callback) {
        mCallback = nullptr;
        return NS_NEVER;
    }
    if (mAwaitBoost) {
        mAwaitBoost = false;
        mLock.unlock();
@@ -1257,26 +1354,26 @@ nsecs_t AudioRecord::processAudioBuffer()
    uint32_t sequence = mSequence;

    // These fields don't need to be cached, because they are assigned only by set():
    //      mTransfer, mCbf, mUserData, mSampleRate, mFrameSize
    //      mTransfer, mCallback, mUserData, mSampleRate, mFrameSize

    mLock.unlock();

    // perform callbacks while unlocked
    if (newOverrun) {
        mCbf(EVENT_OVERRUN, mUserData, NULL);
        callback->onOverrun();

    }
    if (markerReached) {
        mCbf(EVENT_MARKER, mUserData, &markerPosition);
        callback->onMarker(markerPosition.value());
    }
    while (newPosCount > 0) {
        size_t temp = newPosition.value(); // FIXME size_t != uint32_t
        mCbf(EVENT_NEW_POS, mUserData, &temp);
        callback->onNewPos(newPosition.value());
        newPosition += updatePeriod;
        newPosCount--;
    }
    if (mObservedSequence != sequence) {
        mObservedSequence = sequence;
        mCbf(EVENT_NEW_IAUDIORECORD, mUserData, NULL);
        callback->onNewIAudioRecord();
    }

    // if inactive, then don't run me again until re-started
@@ -1370,9 +1467,9 @@ nsecs_t AudioRecord::processAudioBuffer()
                                   mServerConfig.format, audioBuffer.size / mServerSampleSize);
        }

        size_t reqSize = buffer->size;
        mCbf(EVENT_MORE_DATA, mUserData, buffer);
        size_t readSize = buffer->size;
        const size_t reqSize = buffer->size;
        const size_t readSize = callback->onMoreData(*buffer);
        buffer->size = readSize;

        // Validate on returned size
        if (ssize_t(readSize) < 0 || readSize > reqSize) {
+72 −13
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ class AudioRecord : public AudioSystem::AudioDeviceCallback
{
public:

    /* Events used by AudioRecord callback function (callback_t).
    /* Events used by AudioRecord callback function (legacy_callback_t).
     * Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*.
     */
    enum event_type {
@@ -65,7 +65,7 @@ public:
    };

    /* Client should declare a Buffer and pass address to obtainBuffer()
     * and releaseBuffer().  See also callback_t for EVENT_MORE_DATA.
     * and releaseBuffer().  See also legacy_callback_t for EVENT_MORE_DATA.
     */

    class Buffer
@@ -117,7 +117,28 @@ public:
     *          - EVENT_NEW_IAUDIORECORD: unused.
     */

    typedef void (*callback_t)(int event, void* user, void *info);
    typedef void (*legacy_callback_t)(int event, void* user, void *info);

    class IAudioRecordCallback : public virtual RefBase {
        friend AudioRecord;
     protected:
        // Request for client to read newly available data.
        // Used for TRANSFER_CALLBACK mode.
        // Parameters:
        //  - buffer : Buffer to read from
        // Returns:
        //  - Number of bytes actually consumed.
        virtual size_t onMoreData([[maybe_unused]] const AudioRecord::Buffer& buffer) { return 0; }
        // A buffer overrun occurred.
        virtual void onOverrun() {}
        // Record head is at the specified marker (see setMarkerPosition()).
        virtual void onMarker([[maybe_unused]] uint32_t markerPosition) {}
        // Record head is at a new position (see setPositionUpdatePeriod()).
        virtual void onNewPos([[maybe_unused]] uint32_t newPos) {}
        // IAudioRecord was recreated due to re-routing, server invalidation or
        // server crash.
        virtual void onNewIAudioRecord() {}
    };

    /* Returns the minimum frame count required for the successful creation of
     * an AudioRecord object.
@@ -182,20 +203,37 @@ public:
     * pAttributes:        If not NULL, supersedes inputSource for use case selection.
     * threadCanCallJava:  Not present in parameter list, and so is fixed at false.
     */

                        AudioRecord(audio_source_t inputSource,
                                    uint32_t sampleRate,
                                    audio_format_t format,
                                    audio_channel_mask_t channelMask,
                                    const android::content::AttributionSourceState& client,
                                    size_t frameCount = 0,
                                    callback_t cbf = NULL,
                                    void* user = NULL,
                                    const wp<IAudioRecordCallback> &callback = nullptr,
                                    uint32_t notificationFrames = 0,
                                    audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
                                    transfer_type transferType = TRANSFER_DEFAULT,
                                    audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
                                    const audio_attributes_t* pAttributes = nullptr,
                                    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
                                    audio_microphone_direction_t
                                        selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
                                    float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT);


                        AudioRecord(audio_source_t inputSource,
                                    uint32_t sampleRate,
                                    audio_format_t format,
                                    audio_channel_mask_t channelMask,
                                    const android::content::AttributionSourceState& client,
                                    size_t frameCount,
                                    legacy_callback_t callback,
                                    void* user,
                                    uint32_t notificationFrames = 0,
                                    audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
                                    transfer_type transferType = TRANSFER_DEFAULT,
                                    audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
                                    const audio_attributes_t* pAttributes = NULL,
                                    const audio_attributes_t* pAttributes = nullptr,
                                    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
                                    audio_microphone_direction_t
                                        selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
@@ -228,8 +266,7 @@ public:
                            audio_format_t format,
                            audio_channel_mask_t channelMask,
                            size_t frameCount = 0,
                            callback_t cbf = NULL,
                            void* user = NULL,
                            const wp<IAudioRecordCallback> &callback = nullptr,
                            uint32_t notificationFrames = 0,
                            bool threadCanCallJava = false,
                            audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
@@ -237,7 +274,28 @@ public:
                            audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
                            uid_t uid = AUDIO_UID_INVALID,
                            pid_t pid = -1,
                            const audio_attributes_t* pAttributes = NULL,
                            const audio_attributes_t* pAttributes = nullptr,
                            audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
                            audio_microphone_direction_t
                                selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
                            float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT,
                            int32_t maxSharedAudioHistoryMs = 0);

           status_t    set(audio_source_t inputSource,
                            uint32_t sampleRate,
                            audio_format_t format,
                            audio_channel_mask_t channelMask,
                            size_t frameCount,
                            legacy_callback_t callback,
                            void* user,
                            uint32_t notificationFrames = 0,
                            bool threadCanCallJava = false,
                            audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
                            transfer_type transferType = TRANSFER_DEFAULT,
                            audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
                            uid_t uid = AUDIO_UID_INVALID,
                            pid_t pid = -1,
                            const audio_attributes_t* pAttributes = nullptr,
                            audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
                            audio_microphone_direction_t
                                selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
@@ -673,8 +731,9 @@ private:
    bool                    mActive;

    // for client callback handler
    callback_t              mCbf;                   // callback handler for events, or NULL
    void*                   mUserData;

    wp<IAudioRecordCallback> mCallback;
    sp<IAudioRecordCallback> mLegacyCallbackWrapper;

    // for notification APIs
    uint32_t                mNotificationFramesReq; // requested number of frames between each