Report data from aaudio mmap path to audio flinger.

Report data from aaudio mmap path to audio flinger for sound dose
computation. A shared memory is allocated for read and write counter.
The client will shared the write counter with the aaudio service. The
command thread will wake up every 5 burst time to report the data to
audio flinger.

Test: atest AAudioTests
Test: test_marshalling
Test: dump wav file in audio flinger and play
Bug: 264254430
Change-Id: Ib732442c5afc9169fe891212cf77b458c41a87f1
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 9fc503b..876acbc 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -757,6 +757,7 @@
                                audio_port_handle_t *handle);
         virtual status_t stop(audio_port_handle_t handle);
         virtual status_t standby();
+                status_t reportData(const void* buffer, size_t frameCount) override;
 
     private:
         const sp<MmapThread> mThread;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 163e2a0..c617ef7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -9823,6 +9823,10 @@
     return mThread->standby();
 }
 
+status_t AudioFlinger::MmapThreadHandle::reportData(const void* buffer, size_t frameCount) {
+    return mThread->reportData(buffer, frameCount);
+}
+
 
 AudioFlinger::MmapThread::MmapThread(
         const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
@@ -10133,6 +10137,11 @@
     return NO_ERROR;
 }
 
+status_t AudioFlinger::MmapThread::reportData(const void* /*buffer*/, size_t /*frameCount*/) {
+    // This is a stub implementation. The MmapPlaybackThread overrides this function.
+    return INVALID_OPERATION;
+}
+
 
 void AudioFlinger::MmapThread::readHalParameters_l()
 {
@@ -10815,6 +10824,13 @@
     return status;
 }
 
+status_t AudioFlinger::MmapPlaybackThread::reportData(const void* buffer, size_t frameCount) {
+    // TODO(264254430): send the data to mel processor.
+    (void) buffer;
+    (void) frameCount;
+    return NO_ERROR;
+}
+
 void AudioFlinger::MmapPlaybackThread::dumpInternals_l(int fd, const Vector<String16>& args)
 {
     MmapThread::dumpInternals_l(fd, args);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index ddae7ae..47d5333 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -2130,6 +2130,7 @@
     status_t stop(audio_port_handle_t handle);
     status_t standby();
     virtual status_t getExternalPosition(uint64_t *position, int64_t *timeNaos) = 0;
+    virtual status_t reportData(const void* buffer, size_t frameCount);
 
     // RefBase
     virtual     void        onFirstRef();
@@ -2272,6 +2273,8 @@
                                 return !(mOutput == nullptr || mOutput->stream == nullptr);
                             }
 
+                status_t    reportData(const void* buffer, size_t frameCount) override;
+
 protected:
                 void        dumpInternals_l(int fd, const Vector<String16>& args) override;
 
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index d4cdb0b..78e5270 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -58,7 +58,7 @@
     result << "  MMAP: framesTransferred = " << mFramesTransferred.get();
     result << ", HW nanos = " << mHardwareTimeOffsetNanos;
     result << ", port handle = " << mPortHandle;
-    result << ", audio data FD = " << mAudioDataFileDescriptor;
+    result << ", audio data FD = " << mAudioDataWrapper->getDataFileDescriptor();
     result << "\n";
 
     result << "    HW Offset Micros:     " <<
@@ -89,6 +89,7 @@
 
 aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
     aaudio_result_t result = AAUDIO_OK;
+    mAudioDataWrapper = std::make_unique<SharedMemoryWrapper>();
     copyFrom(request.getConstantConfiguration());
     mRequestedDeviceId = getDeviceId();
 
@@ -219,7 +220,7 @@
           __func__, audioFormat, getDeviceId(), getSessionId());
 
     // Create MMAP/NOIRQ buffer.
-    result = createMmapBuffer(&mAudioDataFileDescriptor);
+    result = createMmapBuffer();
     if (result != AAUDIO_OK) {
         goto error;
     }
@@ -243,6 +244,8 @@
     mTimestampGracePeriodMs = ((int64_t) kTimestampGraceBurstCount * mFramesPerBurst
             * AAUDIO_MILLIS_PER_SECOND) / getSampleRate();
 
+    mDataReportOffsetNanos = ((int64_t)mTimestampGracePeriodMs) * AAUDIO_NANOS_PER_MILLISECOND;
+
     ALOGD("%s() got rate = %d, channels = %d channelMask = %#x, deviceId = %d, capacity = %d\n",
           __func__, getSampleRate(), getSamplesPerFrame(), getChannelMask(),
           deviceId, getBufferCapacity());
@@ -327,17 +330,10 @@
     if (mMmapStream == nullptr) {
         return AAUDIO_ERROR_NULL;
     }
-    mAudioDataFileDescriptor.reset();
-    const aaudio_result_t result = createMmapBuffer(&mAudioDataFileDescriptor);
+    mAudioDataWrapper->reset();
+    const aaudio_result_t result = createMmapBuffer();
     if (result == AAUDIO_OK) {
-        const int32_t bytesPerFrame = calculateBytesPerFrame();
-        const int32_t capacityInBytes = getBufferCapacity() * bytesPerFrame;
-        const int fdIndex = parcelable->addFileDescriptor(
-                mAudioDataFileDescriptor, capacityInBytes);
-        parcelable->mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
-        parcelable->mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
-        parcelable->mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
-        parcelable->mDownDataQueueParcelable.setCapacityInFrames(getBufferCapacity());
+        getDownDataDescription(parcelable);
     }
     return result;
 }
@@ -427,14 +423,19 @@
 aaudio_result_t AAudioServiceEndpointMMAP::getDownDataDescription(
         AudioEndpointParcelable* parcelable)
 {
+    if (mAudioDataWrapper->setupFifoBuffer(calculateBytesPerFrame(), getBufferCapacity())
+        != AAUDIO_OK) {
+        ALOGE("Failed to setup audio data wrapper, will not be able to "
+              "set data for sound dose computation");
+        // This will not affect the audio processing capability
+    }
     // Gather information on the data queue based on HAL info.
-    const int32_t bytesPerFrame = calculateBytesPerFrame();
-    const int32_t capacityInBytes = getBufferCapacity() * bytesPerFrame;
-    const int fdIndex = parcelable->addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes);
-    parcelable->mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
-    parcelable->mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
-    parcelable->mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
-    parcelable->mDownDataQueueParcelable.setCapacityInFrames(getBufferCapacity());
+    mAudioDataWrapper->fillParcelable(parcelable, parcelable->mDownDataQueueParcelable,
+                                      calculateBytesPerFrame(), mFramesPerBurst,
+                                      getBufferCapacity(),
+                                      getDirection() == AAUDIO_DIRECTION_OUTPUT
+                                              ? SharedMemoryWrapper::WRITE
+                                              : SharedMemoryWrapper::NONE);
     return AAUDIO_OK;
 }
 
@@ -518,8 +519,7 @@
     return mHalExternalPositionStatus;
 }
 
-aaudio_result_t AAudioServiceEndpointMMAP::createMmapBuffer(
-        android::base::unique_fd* fileDescriptor)
+aaudio_result_t AAudioServiceEndpointMMAP::createMmapBuffer()
 {
     memset(&mMmapBufferinfo, 0, sizeof(struct audio_mmap_buffer_info));
     int32_t minSizeFrames = getBufferCapacity();
@@ -555,8 +555,9 @@
 
     // AAudio creates a copy of this FD and retains ownership of the copy.
     // Assume that AudioFlinger will close the original shared_memory_fd.
-    fileDescriptor->reset(dup(mMmapBufferinfo.shared_memory_fd));
-    if (fileDescriptor->get() == -1) {
+
+    mAudioDataWrapper->getDataFileDescriptor().reset(dup(mMmapBufferinfo.shared_memory_fd));
+    if (mAudioDataWrapper->getDataFileDescriptor().get() == -1) {
         ALOGE("%s() - could not dup shared_memory_fd", __func__);
         return AAUDIO_ERROR_INTERNAL;
     }
@@ -571,3 +572,31 @@
 
     return AAUDIO_OK;
 }
+
+int64_t AAudioServiceEndpointMMAP::nextDataReportTime() {
+    return getDirection() == AAUDIO_DIRECTION_OUTPUT
+            ? AudioClock::getNanoseconds() + mDataReportOffsetNanos
+            : std::numeric_limits<int64_t>::max();
+}
+
+void AAudioServiceEndpointMMAP::reportData() {
+    if (mMmapStream == nullptr) {
+        // This must not happen
+        ALOGE("%s() invalid state, mmap stream is not initialized", __func__);
+        return;
+    }
+    auto fifo = mAudioDataWrapper->getFifoBuffer();
+    if (fifo == nullptr) {
+        ALOGE("%s() fifo buffer is not initialized, cannot report data", __func__);
+        return;
+    }
+
+    WrappingBuffer wrappingBuffer;
+    fifo_frames_t framesAvailable = fifo->getFullDataAvailable(&wrappingBuffer);
+    for (size_t i = 0; i < WrappingBuffer::SIZE; ++i) {
+        if (wrappingBuffer.numFrames[i] > 0) {
+            mMmapStream->reportData(wrappingBuffer.data[i], wrappingBuffer.numFrames[i]);
+        }
+    }
+    fifo->advanceReadIndex(framesAvailable);
+}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index 4f77393..38cf0ba 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -30,6 +30,7 @@
 #include "AAudioServiceStreamMMAP.h"
 #include "AAudioMixer.h"
 #include "AAudioService.h"
+#include "SharedMemoryWrapper.h"
 
 namespace aaudio {
 
@@ -90,11 +91,15 @@
 
     aaudio_result_t getExternalPosition(uint64_t *positionFrames, int64_t *timeNanos);
 
+    int64_t nextDataReportTime();
+
+    void reportData();
+
 private:
 
     aaudio_result_t openWithFormat(audio_format_t audioFormat, audio_format_t* nextFormatToTry);
 
-    aaudio_result_t createMmapBuffer(android::base::unique_fd* fileDescriptor);
+    aaudio_result_t createMmapBuffer();
 
     MonotonicCounter                          mFramesTransferred;
 
@@ -107,7 +112,7 @@
 
     android::AAudioService                    &mAAudioService;
 
-    android::base::unique_fd                  mAudioDataFileDescriptor;
+    std::unique_ptr<SharedMemoryWrapper>      mAudioDataWrapper;
 
     int64_t                                   mHardwareTimeOffsetNanos = 0; // TODO get from HAL
 
@@ -117,6 +122,7 @@
     int32_t                                   mTimestampGracePeriodMs;
     int32_t                                   mFrozenPositionCount = 0;
     int32_t                                   mFrozenTimestampCount = 0;
+    int64_t                                   mDataReportOffsetNanos = 0;
 
 };
 
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 8e1e497..51ef2d9 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -404,7 +404,8 @@
     // Hold onto the ref counted stream until the end.
     android::sp<AAudioServiceStreamBase> holdStream(this);
     TimestampScheduler timestampScheduler;
-    int64_t nextTime;
+    int64_t nextTimestampReportTime;
+    int64_t nextDataReportTime;
     int64_t standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS;
     // Balance the incStrong from when the thread was launched.
     holdStream->decStrong(nullptr);
@@ -418,7 +419,8 @@
         loopCount++;
         int64_t timeoutNanos = -1;
         if (isRunning() || (isIdle_l() && !isStandby_l())) {
-            timeoutNanos = (isRunning() ? nextTime : standbyTime) - AudioClock::getNanoseconds();
+            timeoutNanos = (isRunning() ? std::min(nextTimestampReportTime, nextDataReportTime)
+                                        : standbyTime) - AudioClock::getNanoseconds();
             timeoutNanos = std::max<int64_t>(0, timeoutNanos);
         }
 
@@ -428,13 +430,20 @@
             break;
         }
 
-        if (isRunning() && AudioClock::getNanoseconds() >= nextTime) {
-            // It is time to update timestamp.
-            if (sendCurrentTimestamp_l() != AAUDIO_OK) {
-                ALOGE("Failed to send current timestamp, stop updating timestamp");
-                disconnect_l();
-            } else {
-                nextTime = timestampScheduler.nextAbsoluteTime();
+        if (isRunning()) {
+            auto currentTimestamp = AudioClock::getNanoseconds();
+            if (currentTimestamp >= nextDataReportTime) {
+                reportData_l();
+                nextDataReportTime = nextDataReportTime_l();
+            }
+            if (currentTimestamp >= nextTimestampReportTime) {
+                // It is time to update timestamp.
+                if (sendCurrentTimestamp_l() != AAUDIO_OK) {
+                    ALOGE("Failed to send current timestamp, stop updating timestamp");
+                    disconnect_l();
+                } else {
+                    nextTimestampReportTime = timestampScheduler.nextAbsoluteTime();
+                }
             }
         }
         if (isIdle_l() && AudioClock::getNanoseconds() >= standbyTime) {
@@ -456,7 +465,8 @@
                     command->result = start_l();
                     timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate());
                     timestampScheduler.start(AudioClock::getNanoseconds());
-                    nextTime = timestampScheduler.nextAbsoluteTime();
+                    nextTimestampReportTime = timestampScheduler.nextAbsoluteTime();
+                    nextDataReportTime = nextDataReportTime_l();
                     break;
                 case PAUSE:
                     command->result = pause_l();
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 0f51503..bc7ccde 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -346,6 +346,11 @@
                 || mState == AAUDIO_STREAM_STATE_STOPPED;
     }
 
+    virtual int64_t nextDataReportTime_l() REQUIRES(mLock) {
+        return std::numeric_limits<int64_t>::max();
+    }
+    virtual void reportData_l() REQUIRES(mLock) { return; }
+
     pid_t                   mRegisteredClientThread = ILLEGAL_THREAD_ID;
 
     std::mutex              mUpMessageQueueLock;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index ec9b2e2..89f6e33 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -238,3 +238,25 @@
             static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
     return serviceEndpointMMAP->getDownDataDescription(parcelable);
 }
+
+int64_t AAudioServiceStreamMMAP::nextDataReportTime_l() {
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return std::numeric_limits<int64_t>::max();
+    }
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
+            static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
+    return serviceEndpointMMAP->nextDataReportTime();
+}
+
+void AAudioServiceStreamMMAP::reportData_l() {
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return;
+    }
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
+            static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
+    return serviceEndpointMMAP->reportData();
+}
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index 8b8c5e6..db3c8d0 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -84,6 +84,10 @@
     aaudio_result_t getHardwareTimestamp_l(
             int64_t *positionFrames, int64_t *timeNanos) REQUIRES(mLock) override;
 
+    int64_t nextDataReportTime_l() REQUIRES(mLock) override;
+
+    void reportData_l() REQUIRES(mLock) override;
+
     /**
      * Device specific startup.
      * @return AAUDIO_OK or negative error.
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index 56c0dc9..c5080a4 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -104,6 +104,7 @@
         "AAudioStreamTracker.cpp",
         "AAudioThread.cpp",
         "SharedMemoryProxy.cpp",
+        "SharedMemoryWrapper.cpp",
         "SharedRingBuffer.cpp",
         "TimestampScheduler.cpp",
     ],
diff --git a/services/oboeservice/SharedMemoryWrapper.cpp b/services/oboeservice/SharedMemoryWrapper.cpp
new file mode 100644
index 0000000..c0dcccb
--- /dev/null
+++ b/services/oboeservice/SharedMemoryWrapper.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SharedMemoryWrapper"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sys/mman.h>
+
+#include "SharedMemoryWrapper.h"
+
+namespace aaudio {
+
+constexpr int COUNTER_SIZE_IN_BYTES = sizeof(android::fifo_counter_t);
+constexpr int WRAPPER_SIZE_IN_BYTES = 2 * COUNTER_SIZE_IN_BYTES;
+
+SharedMemoryWrapper::SharedMemoryWrapper() {
+    mCounterFd.reset(ashmem_create_region("AAudioSharedMemoryWrapper", WRAPPER_SIZE_IN_BYTES));
+    if (mCounterFd.get() == -1) {
+        ALOGE("allocate() ashmem_create_region() failed %d", errno);
+        return;
+    }
+    int err = ashmem_set_prot_region(mCounterFd.get(), PROT_READ|PROT_WRITE);
+    if (err < 0) {
+        ALOGE("allocate() ashmem_set_prot_region() failed %d", errno);
+        mCounterFd.reset();
+        return;
+    }
+    auto tmpPtr = (uint8_t *) mmap(nullptr, WRAPPER_SIZE_IN_BYTES,
+                                   PROT_READ|PROT_WRITE,
+                                   MAP_SHARED,
+                                   mCounterFd.get(), 0);
+    if (tmpPtr == MAP_FAILED) {
+        ALOGE("allocate() mmap() failed %d", errno);
+        mCounterFd.reset();
+        return;
+    }
+    mCounterMemoryAddress = tmpPtr;
+
+    mReadCounterAddress = (android::fifo_counter_t*) mCounterMemoryAddress;
+    mWriteCounterAddress = (android::fifo_counter_t*) &mCounterMemoryAddress[COUNTER_SIZE_IN_BYTES];
+}
+
+SharedMemoryWrapper::~SharedMemoryWrapper()
+{
+    reset();
+    if (mCounterMemoryAddress != nullptr) {
+        munmap(mCounterMemoryAddress, COUNTER_SIZE_IN_BYTES);
+        mCounterMemoryAddress = nullptr;
+    }
+}
+
+aaudio_result_t SharedMemoryWrapper::setupFifoBuffer(android::fifo_frames_t bytesPerFrame,
+                                                     android::fifo_frames_t capacityInFrames) {
+    if (mDataFd.get() == -1) {
+        ALOGE("%s data file descriptor is not initialized", __func__);
+        return AAUDIO_ERROR_INTERNAL;
+    }
+    if (mCounterMemoryAddress == nullptr) {
+        ALOGE("%s the counter memory is not allocated correctly", __func__);
+        return AAUDIO_ERROR_INTERNAL;
+    }
+    mSharedMemorySizeInBytes = bytesPerFrame * capacityInFrames;
+    auto tmpPtr = (uint8_t *) mmap(nullptr, mSharedMemorySizeInBytes,
+                                   PROT_READ|PROT_WRITE,
+                                   MAP_SHARED,
+                                   mDataFd.get(), 0);
+    if (tmpPtr == MAP_FAILED) {
+        ALOGE("allocate() mmap() failed %d", errno);
+        return AAUDIO_ERROR_INTERNAL;
+    }
+    mSharedMemory = tmpPtr;
+
+    mFifoBuffer = std::make_shared<android::FifoBufferIndirect>(
+            bytesPerFrame, capacityInFrames, mReadCounterAddress,
+            mWriteCounterAddress, mSharedMemory);
+    return AAUDIO_OK;
+}
+
+void SharedMemoryWrapper::reset() {
+    mFifoBuffer.reset();
+    if (mSharedMemory != nullptr) {
+        munmap(mSharedMemory, mSharedMemorySizeInBytes);
+        mSharedMemory = nullptr;
+    }
+    mDataFd.reset();
+}
+
+void SharedMemoryWrapper::fillParcelable(
+        AudioEndpointParcelable* endpointParcelable, RingBufferParcelable &ringBufferParcelable,
+        int32_t bytesPerFrame, int32_t framesPerBurst, int32_t capacityInFrames,
+        CounterFilling counterFilling) {
+    const int capacityInBytes = bytesPerFrame * capacityInFrames;
+    const int dataFdIndex =
+                endpointParcelable->addFileDescriptor(mDataFd, mSharedMemorySizeInBytes);
+    ringBufferParcelable.setBytesPerFrame(bytesPerFrame);
+    ringBufferParcelable.setFramesPerBurst(framesPerBurst);
+    ringBufferParcelable.setCapacityInFrames(capacityInFrames);
+    if (mCounterFd.get() == -1 || counterFilling == NONE) {
+        // Failed to create shared memory for read/write counter or requesting no filling counters.
+        ALOGD("%s no counter is filled, counterFd=%d", __func__, mCounterFd.get());
+        ringBufferParcelable.setupMemory(dataFdIndex, 0, capacityInBytes);
+    } else {
+        int counterFdIndex =
+                endpointParcelable->addFileDescriptor(mCounterFd, WRAPPER_SIZE_IN_BYTES);
+        const int readCounterSize = (counterFilling & READ) == NONE ? 0 : COUNTER_SIZE_IN_BYTES;
+        const int writeCounterSize = (counterFilling & WRITE) == NONE ? 0 : COUNTER_SIZE_IN_BYTES;
+        ALOGD("%s counterFdIndex=%d readCounterSize=%d, writeCounterSize=%d",
+              __func__, counterFdIndex, readCounterSize, writeCounterSize);
+        ringBufferParcelable.setupMemory(
+                {dataFdIndex, 0 /*offset*/, capacityInBytes},
+                {counterFdIndex, 0 /*offset*/, readCounterSize},
+                {counterFdIndex, COUNTER_SIZE_IN_BYTES, writeCounterSize});
+    }
+}
+
+} // namespace aaudio
diff --git a/services/oboeservice/SharedMemoryWrapper.h b/services/oboeservice/SharedMemoryWrapper.h
new file mode 100644
index 0000000..323c7f1
--- /dev/null
+++ b/services/oboeservice/SharedMemoryWrapper.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <cutils/ashmem.h>
+#include <stdint.h>
+#include <string>
+#include <sys/mman.h>
+
+#include "fifo/FifoBuffer.h"
+#include "binding/RingBufferParcelable.h"
+#include "binding/AudioEndpointParcelable.h"
+
+namespace aaudio {
+
+/**
+ * Wrap the shared memory with read and write counters. Provide a fifo buffer to access the
+ * wrapped shared memory.
+ */
+class SharedMemoryWrapper {
+public:
+    explicit SharedMemoryWrapper();
+
+    virtual ~SharedMemoryWrapper();
+
+    android::base::unique_fd& getDataFileDescriptor() { return mDataFd; }
+
+    aaudio_result_t setupFifoBuffer(android::fifo_frames_t bytesPerFrame,
+                                    android::fifo_frames_t capacityInFrames);
+
+    void reset();
+
+    enum CounterFilling {
+        NONE = 0,
+        READ = 1,
+        WRITE = 2,
+    };
+    /**
+     * Fill shared memory into parcelable.
+     *
+     * @param endpointParcelable container for ring buffers and shared memories
+     * @param ringBufferParcelable the ring buffer
+     * @param bytesPerFrame the bytes per frame of the data memory
+     * @param framesPerBurst the frame per burst of the data memory
+     * @param capacityInFrames the capacity in frames of the data memory
+     * @param counterFilling a bit mask to control if the counter from the wrapper should be filled
+     *                       or not.
+     */
+    void fillParcelable(AudioEndpointParcelable* endpointParcelable,
+                        RingBufferParcelable &ringBufferParcelable,
+                        int32_t bytesPerFrame,
+                        int32_t framesPerBurst,
+                        int32_t capacityInFrames,
+                        CounterFilling counterFilling = NONE);
+
+    std::shared_ptr<android::FifoBuffer> getFifoBuffer() {
+        return mFifoBuffer;
+    }
+
+private:
+    android::base::unique_fd mDataFd;
+    android::base::unique_fd mCounterFd;
+    uint8_t* mCounterMemoryAddress = nullptr;
+    android::fifo_counter_t* mReadCounterAddress = nullptr;
+    android::fifo_counter_t* mWriteCounterAddress = nullptr;
+    std::shared_ptr<android::FifoBufferIndirect> mFifoBuffer;
+    uint8_t* mSharedMemory = nullptr;
+    int32_t mSharedMemorySizeInBytes = 0;
+};
+
+} /* namespace aaudio */