Add API to get the shared memory Audio/Video handle

In the MediaEvent of AV filters, the AV memory handle is mapped/unmapped on each event.
If the vendor allocates a persistent memory block, and use MediaEvent.offset to access the data,
the mapping change is unnecessary.

To improve this, a new API to get shared AV Memory is introduced.
To pass additional information to Codec2 for AV memory,
the index for Codec2 to setup additional information can be carried in the handle.

Test: atest VtsHalTvTunerV1_1TargetTest
Bug: 162013047
Bug: 157143515
Bug: 156664393
Change-Id: Ibd4471ff8e19b0a99b8bcbc982dc08dbcc92efcf
diff --git a/tv/tuner/1.1/IFilter.hal b/tv/tuner/1.1/IFilter.hal
index b362e32..2ba9bb7 100644
--- a/tv/tuner/1.1/IFilter.hal
+++ b/tv/tuner/1.1/IFilter.hal
@@ -51,4 +51,23 @@
      *         UNKNOWN_ERROR if failed for other reasons.
      */
     configureIpCid(uint32_t ipCid) generates (Result result);
+
+    /**
+     * Get the shared AV memory handle. Use IFilter.releaseAvHandle to release the handle.
+     *
+     * When media filters are opened, call this API to initialize the share memory handle if it's
+     * needed.
+     *
+     * If DemuxFilterMediaEvent.avMemory contains file descriptor, share memory should be ignored.
+     *
+     * @return avMemory A handle associated to the shared memory for audio or video.
+     *         avMemory.data[0] is normally an fd for ION memory. When the avMemory->numFd is 0, the
+     *         share memory is not initialized and does not contain valid fd.
+     *         avMemory.data[avMemory.numFds] is an index used as a parameter of
+     *         C2DataIdInfo to build C2 buffer in Codec. No C2 buffer would be created if the index
+     *         does not exist.
+     * @return avMemSize the size of the shared av memory. It should be ignored when the share
+     *         memory is not initialized.
+     */
+    getAvSharedHandle() generates (Result result, handle avMemory, uint64_t avMemSize);
 };
diff --git a/tv/tuner/1.1/default/Filter.cpp b/tv/tuner/1.1/default/Filter.cpp
index 3db8b9e..c69beca 100644
--- a/tv/tuner/1.1/default/Filter.cpp
+++ b/tv/tuner/1.1/default/Filter.cpp
@@ -163,8 +163,17 @@
     return Result::SUCCESS;
 }
 
-Return<Result> Filter::releaseAvHandle(const hidl_handle& /*avMemory*/, uint64_t avDataId) {
+Return<Result> Filter::releaseAvHandle(const hidl_handle& avMemory, uint64_t avDataId) {
     ALOGV("%s", __FUNCTION__);
+
+    if ((avMemory.getNativeHandle()->numFds > 0) &&
+        (mSharedAvMemHandle.getNativeHandle()->numFds > 0) &&
+        (sameFile(avMemory.getNativeHandle()->data[0],
+                  mSharedAvMemHandle.getNativeHandle()->data[0]))) {
+        freeSharedAvHandle();
+        return Result::SUCCESS;
+    }
+
     if (mDataId2Avfd.find(avDataId) == mDataId2Avfd.end()) {
         return Result::INVALID_ARGUMENT;
     }
@@ -190,6 +199,35 @@
     return Result::SUCCESS;
 }
 
+Return<void> Filter::getAvSharedHandle(getAvSharedHandle_cb _hidl_cb) {
+    ALOGV("%s", __FUNCTION__);
+
+    if (!mIsMediaFilter) {
+        _hidl_cb(Result::INVALID_STATE, NULL, BUFFER_SIZE_16M);
+        return Void();
+    }
+
+    if (mSharedAvMemHandle.getNativeHandle() != nullptr) {
+        _hidl_cb(Result::SUCCESS, mSharedAvMemHandle, BUFFER_SIZE_16M);
+        return Void();
+    }
+
+    int av_fd = createAvIonFd(BUFFER_SIZE_16M);
+    if (av_fd == -1) {
+        _hidl_cb(Result::UNKNOWN_ERROR, NULL, 0);
+    }
+
+    native_handle_t* nativeHandle = createNativeHandle(av_fd);
+    if (nativeHandle == NULL) {
+        _hidl_cb(Result::UNKNOWN_ERROR, NULL, 0);
+    }
+    mSharedAvMemHandle.setTo(nativeHandle, /*shouldOwn=*/true);
+    ::close(av_fd);
+
+    _hidl_cb(Result::SUCCESS, mSharedAvMemHandle, BUFFER_SIZE_16M);
+    return Void();
+}
+
 bool Filter::createFilterMQ() {
     ALOGV("%s", __FUNCTION__);
 
@@ -313,10 +351,19 @@
     }
     for (int i = 0; i < mFilterEvent.events.size(); i++) {
         ::close(mFilterEvent.events[i].media().avMemory.getNativeHandle()->data[0]);
-        native_handle_close(mFilterEvent.events[i].media().avMemory.getNativeHandle());
+        native_handle_delete(const_cast<native_handle_t*>(
+                mFilterEvent.events[i].media().avMemory.getNativeHandle()));
     }
 }
 
+void Filter::freeSharedAvHandle() {
+    if (!mIsMediaFilter) {
+        return;
+    }
+    ::close(mSharedAvMemHandle.getNativeHandle()->data[0]);
+    native_handle_delete(const_cast<native_handle_t*>(mSharedAvMemHandle.getNativeHandle()));
+}
+
 void Filter::maySendFilterStatusCallback() {
     if (!mIsUsingFMQ) {
         return;
@@ -509,8 +556,8 @@
         return Result::SUCCESS;
     }
 
+    Result result;
     if (mPts) {
-        Result result;
         result = createMediaFilterEventWithIon(mFilterOutput);
         if (result == Result::SUCCESS) {
             mFilterOutput.clear();
@@ -551,7 +598,10 @@
             continue;
         }
 
-        createMediaFilterEventWithIon(mPesOutput);
+        result = createMediaFilterEventWithIon(mPesOutput);
+        if (result != Result::SUCCESS) {
+            return result;
+        }
     }
 
     mFilterOutput.clear();
@@ -560,51 +610,14 @@
 }
 
 Result Filter::createMediaFilterEventWithIon(vector<uint8_t> output) {
-    int av_fd = createAvIonFd(output.size());
-    if (av_fd == -1) {
-        return Result::UNKNOWN_ERROR;
+    if (mUsingSharedAvMem) {
+        if (mSharedAvMemHandle.getNativeHandle() == nullptr) {
+            return Result::UNKNOWN_ERROR;
+        }
+        return createShareMemMediaEvents(output);
     }
-    // copy the filtered data to the buffer
-    uint8_t* avBuffer = getIonBuffer(av_fd, output.size());
-    if (avBuffer == NULL) {
-        return Result::UNKNOWN_ERROR;
-    }
-    memcpy(avBuffer, output.data(), output.size() * sizeof(uint8_t));
 
-    native_handle_t* nativeHandle = createNativeHandle(av_fd);
-    if (nativeHandle == NULL) {
-        return Result::UNKNOWN_ERROR;
-    }
-    hidl_handle handle;
-    handle.setTo(nativeHandle, /*shouldOwn=*/true);
-
-    // Create a dataId and add a <dataId, av_fd> pair into the dataId2Avfd map
-    uint64_t dataId = mLastUsedDataId++ /*createdUID*/;
-    mDataId2Avfd[dataId] = dup(av_fd);
-
-    // Create mediaEvent and send callback
-    DemuxFilterMediaEvent mediaEvent;
-    mediaEvent = {
-            .avMemory = std::move(handle),
-            .dataLength = static_cast<uint32_t>(output.size()),
-            .avDataId = dataId,
-    };
-    if (mPts) {
-        mediaEvent.pts = mPts;
-        mPts = 0;
-    }
-    int size = mFilterEvent.events.size();
-    mFilterEvent.events.resize(size + 1);
-    mFilterEvent.events[size].media(mediaEvent);
-
-    // Clear and log
-    output.clear();
-    mAvBufferCopyCount = 0;
-    ::close(av_fd);
-    if (DEBUG_FILTER) {
-        ALOGD("[Filter] av data length %d", mediaEvent.dataLength);
-    }
-    return Result::SUCCESS;
+    return createIndependentMediaEvents(output);
 }
 
 Result Filter::startRecordFilterHandler() {
@@ -713,15 +726,119 @@
 }
 
 native_handle_t* Filter::createNativeHandle(int fd) {
-    // Create a native handle to pass the av fd via the callback event.
-    native_handle_t* nativeHandle = native_handle_create(/*numFd*/ 1, 0);
+    native_handle_t* nativeHandle;
+    if (fd < 0) {
+        nativeHandle = native_handle_create(/*numFd*/ 0, 0);
+    } else {
+        // Create a native handle to pass the av fd via the callback event.
+        nativeHandle = native_handle_create(/*numFd*/ 1, 0);
+    }
     if (nativeHandle == NULL) {
         ALOGE("[Filter] Failed to create native_handle %d", errno);
         return NULL;
     }
-    nativeHandle->data[0] = dup(fd);
+    if (nativeHandle->numFds > 0) {
+        nativeHandle->data[0] = dup(fd);
+    }
     return nativeHandle;
 }
+
+Result Filter::createIndependentMediaEvents(vector<uint8_t> output) {
+    int av_fd = createAvIonFd(output.size());
+    if (av_fd == -1) {
+        return Result::UNKNOWN_ERROR;
+    }
+    // copy the filtered data to the buffer
+    uint8_t* avBuffer = getIonBuffer(av_fd, output.size());
+    if (avBuffer == NULL) {
+        return Result::UNKNOWN_ERROR;
+    }
+    memcpy(avBuffer, output.data(), output.size() * sizeof(uint8_t));
+
+    native_handle_t* nativeHandle = createNativeHandle(av_fd);
+    if (nativeHandle == NULL) {
+        return Result::UNKNOWN_ERROR;
+    }
+    hidl_handle handle;
+    handle.setTo(nativeHandle, /*shouldOwn=*/true);
+
+    // Create a dataId and add a <dataId, av_fd> pair into the dataId2Avfd map
+    uint64_t dataId = mLastUsedDataId++ /*createdUID*/;
+    mDataId2Avfd[dataId] = dup(av_fd);
+
+    // Create mediaEvent and send callback
+    DemuxFilterMediaEvent mediaEvent;
+    mediaEvent = {
+            .avMemory = std::move(handle),
+            .dataLength = static_cast<uint32_t>(output.size()),
+            .avDataId = dataId,
+    };
+    if (mPts) {
+        mediaEvent.pts = mPts;
+        mPts = 0;
+    }
+    int size = mFilterEvent.events.size();
+    mFilterEvent.events.resize(size + 1);
+    mFilterEvent.events[size].media(mediaEvent);
+
+    // Clear and log
+    output.clear();
+    mAvBufferCopyCount = 0;
+    ::close(av_fd);
+    if (DEBUG_FILTER) {
+        ALOGD("[Filter] av data length %d", mediaEvent.dataLength);
+    }
+    return Result::SUCCESS;
+}
+
+Result Filter::createShareMemMediaEvents(vector<uint8_t> output) {
+    // copy the filtered data to the shared buffer
+    uint8_t* sharedAvBuffer = getIonBuffer(mSharedAvMemHandle.getNativeHandle()->data[0],
+                                           output.size() + mSharedAvMemOffset);
+    if (sharedAvBuffer == NULL) {
+        return Result::UNKNOWN_ERROR;
+    }
+    memcpy(sharedAvBuffer + mSharedAvMemOffset, output.data(), output.size() * sizeof(uint8_t));
+
+    // Create a memory handle with numFds == 0
+    native_handle_t* nativeHandle = createNativeHandle(-1);
+    if (nativeHandle == NULL) {
+        return Result::UNKNOWN_ERROR;
+    }
+    hidl_handle handle;
+    handle.setTo(nativeHandle, /*shouldOwn=*/true);
+
+    // Create mediaEvent and send callback
+    DemuxFilterMediaEvent mediaEvent;
+    mediaEvent = {
+            .offset = static_cast<uint32_t>(mSharedAvMemOffset),
+            .dataLength = static_cast<uint32_t>(output.size()),
+            .avMemory = handle,
+    };
+    mSharedAvMemOffset += output.size();
+    if (mPts) {
+        mediaEvent.pts = mPts;
+        mPts = 0;
+    }
+    int size = mFilterEvent.events.size();
+    mFilterEvent.events.resize(size + 1);
+    mFilterEvent.events[size].media(mediaEvent);
+
+    // Clear and log
+    output.clear();
+    if (DEBUG_FILTER) {
+        ALOGD("[Filter] shared av data length %d", mediaEvent.dataLength);
+    }
+    return Result::SUCCESS;
+}
+
+bool Filter::sameFile(int fd1, int fd2) {
+    struct stat stat1, stat2;
+    if (fstat(fd1, &stat1) < 0 || fstat(fd2, &stat2) < 0) {
+        return false;
+    }
+    return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
+}
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace tuner
diff --git a/tv/tuner/1.1/default/Filter.h b/tv/tuner/1.1/default/Filter.h
index 23bc25c..f677666 100644
--- a/tv/tuner/1.1/default/Filter.h
+++ b/tv/tuner/1.1/default/Filter.h
@@ -23,6 +23,7 @@
 #include <inttypes.h>
 #include <ion/ion.h>
 #include <math.h>
+#include <sys/stat.h>
 #include <set>
 #include "Demux.h"
 #include "Dvr.h"
@@ -43,6 +44,7 @@
 using ::android::hardware::MQDescriptorSync;
 
 using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+const uint32_t BUFFER_SIZE_16M = 0x1000000;
 
 class Demux;
 class Dvr;
@@ -78,6 +80,8 @@
 
     virtual Return<Result> configureIpCid(uint32_t ipCid) override;
 
+    virtual Return<void> getAvSharedHandle(getAvSharedHandle_cb _hidl_cb) override;
+
     /**
      * To create a FilterMQ and its Event Flag.
      *
@@ -93,6 +97,7 @@
     void attachFilterToRecord(const sp<Dvr> dvr);
     void detachFilterFromRecord();
     void freeAvHandle();
+    void freeSharedAvHandle();
     bool isMediaFilter() { return mIsMediaFilter; };
     bool isPcrFilter() { return mIsPcrFilter; };
     bool isRecordFilter() { return mIsRecordFilter; };
@@ -185,6 +190,9 @@
     uint8_t* getIonBuffer(int fd, int size);
     native_handle_t* createNativeHandle(int fd);
     Result createMediaFilterEventWithIon(vector<uint8_t> output);
+    Result createIndependentMediaEvents(vector<uint8_t> output);
+    Result createShareMemMediaEvents(vector<uint8_t> output);
+    bool sameFile(int fd1, int fd2);
 
     /**
      * Lock to protect writes to the FMQs
@@ -212,6 +220,11 @@
     std::map<uint64_t, int> mDataId2Avfd;
     uint64_t mLastUsedDataId = 1;
     int mAvBufferCopyCount = 0;
+
+    // Shared A/V memory handle
+    hidl_handle mSharedAvMemHandle;
+    bool mUsingSharedAvMem = true;
+    uint32_t mSharedAvMemOffset = 0;
 };
 
 }  // namespace implementation
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.cpp b/tv/tuner/1.1/vts/functional/FilterTests.cpp
index 7e5b549..5a8985d 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.1/vts/functional/FilterTests.cpp
@@ -16,22 +16,70 @@
 
 #include "FilterTests.h"
 
-bool FilterCallback::readFilterEventData() {
-    bool result = false;
-    ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId);
+void FilterCallback::testFilterDataOutput() {
+    android::Mutex::Autolock autoLock(mMsgLock);
+    while (mPidFilterOutputCount < 1) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            EXPECT_TRUE(false) << "filter output matching pid does not output within timeout";
+            return;
+        }
+    }
+    mPidFilterOutputCount = 0;
+    ALOGW("[vts] pass and stop");
+}
+
+void FilterCallback::readFilterEventData() {
+    ALOGW("[vts] reading filter event");
     // todo separate filter handlers
+    for (int i = 0; i < mFilterEvent.events.size(); i++) {
+        auto event = mFilterEvent.events[i];
+        switch (event.getDiscriminator()) {
+            case DemuxFilterEvent::Event::hidl_discriminator::media:
+                ALOGD("[vts] Media filter event, avMemHandle numFds=%d.",
+                      event.media().avMemory.getNativeHandle()->numFds);
+                dumpAvData(event.media());
+                break;
+            default:
+                break;
+        }
+    }
     for (int i = 0; i < mFilterEventExt.events.size(); i++) {
         auto eventExt = mFilterEventExt.events[i];
         switch (eventExt.getDiscriminator()) {
             case DemuxFilterEventExt::Event::hidl_discriminator::tsRecord:
-                ALOGW("[vts] Extended TS record filter event, pts=%" PRIu64 ".",
+                ALOGD("[vts] Extended TS record filter event, pts=%" PRIu64 ".",
                       eventExt.tsRecord().pts);
                 break;
             default:
                 break;
         }
     }
-    return result;
+}
+
+bool FilterCallback::dumpAvData(DemuxFilterMediaEvent event) {
+    uint32_t length = event.dataLength;
+    uint32_t offset = event.offset;
+    // read data from buffer pointed by a handle
+    hidl_handle handle = event.avMemory;
+    if (handle.getNativeHandle()->numFds == 0) {
+        if (mAvSharedHandle == NULL) {
+            return false;
+        }
+        handle = mAvSharedHandle;
+    }
+
+    int av_fd = handle.getNativeHandle()->data[0];
+    uint8_t* buffer =
+            static_cast<uint8_t*>(mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, av_fd, 0));
+    if (buffer == MAP_FAILED) {
+        ALOGE("[vts] fail to allocate av buffer, errno=%d", errno);
+        return false;
+    }
+    uint8_t output[length + 1];
+    memcpy(output, buffer + offset, length);
+    // print buffer and check with golden output.
+    ::close(av_fd);
+    return true;
 }
 
 AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type, uint32_t bufferSize) {
@@ -81,6 +129,33 @@
     return AssertionResult(status == Result::SUCCESS);
 }
 
+AssertionResult FilterTests::getSharedAvMemoryHandle(uint64_t filterId) {
+    EXPECT_TRUE(mFilters[filterId]) << "Open media filter first.";
+    Result status = Result::UNKNOWN_ERROR;
+    sp<android::hardware::tv::tuner::V1_1::IFilter> filter_v1_1 =
+            android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilters[filterId]);
+    if (filter_v1_1 != NULL) {
+        filter_v1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
+            status = r;
+            if (status == Result::SUCCESS) {
+                mFilterCallbacks[mFilterId]->setSharedHandle(avMemory);
+                mFilterCallbacks[mFilterId]->setMemSize(avMemSize);
+                mAvSharedHandle = avMemory;
+            }
+        });
+    }
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult FilterTests::releaseShareAvHandle(uint64_t filterId) {
+    Result status;
+    EXPECT_TRUE(mFilters[filterId]) << "Open media filter first.";
+    EXPECT_TRUE(mAvSharedHandle) << "No shared av handle to release.";
+    status = mFilters[filterId]->releaseAvHandle(mAvSharedHandle, 0 /*dataId*/);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
 AssertionResult FilterTests::configFilter(DemuxFilterSettings setting, uint64_t filterId) {
     Result status;
     EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.h b/tv/tuner/1.1/vts/functional/FilterTests.h
index a47f631..bc6db86 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.h
+++ b/tv/tuner/1.1/vts/functional/FilterTests.h
@@ -46,6 +46,7 @@
 using android::hardware::Void;
 using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
 using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using android::hardware::tv::tuner::V1_0::DemuxFilterMediaEvent;
 using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
 using android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
 using android::hardware::tv::tuner::V1_0::DemuxFilterType;
@@ -93,7 +94,14 @@
     }
 
     virtual Return<void> onFilterEvent(
-            const android::hardware::tv::tuner::V1_0::DemuxFilterEvent& /*filterEvent*/) override {
+            const android::hardware::tv::tuner::V1_0::DemuxFilterEvent& filterEvent) override {
+        android::Mutex::Autolock autoLock(mMsgLock);
+        // Temprarily we treat the first coming back filter data on the matching pid a success
+        // once all of the MQ are cleared, means we got all the expected output
+        mFilterEvent = filterEvent;
+        readFilterEventData();
+        mPidFilterOutputCount++;
+        mMsgCondition.signal();
         return Void();
     }
 
@@ -104,8 +112,13 @@
     void setFilterId(uint32_t filterId) { mFilterId = filterId; }
     void setFilterInterface(sp<IFilter> filter) { mFilter = filter; }
     void setFilterEventType(FilterEventType type) { mFilterEventType = type; }
+    void setSharedHandle(hidl_handle sharedHandle) { mAvSharedHandle = sharedHandle; }
+    void setMemSize(uint64_t size) { mAvSharedMemSize = size; }
 
-    bool readFilterEventData();
+    void testFilterDataOutput();
+
+    void readFilterEventData();
+    bool dumpAvData(DemuxFilterMediaEvent event);
 
   private:
     uint32_t mFilterId;
@@ -114,6 +127,9 @@
     DemuxFilterEvent mFilterEvent;
     DemuxFilterEventExt mFilterEventExt;
 
+    hidl_handle mAvSharedHandle = NULL;
+    uint64_t mAvSharedMemSize = -1;
+
     android::Mutex mMsgLock;
     android::Mutex mFilterOutputLock;
     android::Condition mMsgCondition;
@@ -127,10 +143,12 @@
     void setDemux(sp<IDemux> demux) { mDemux = demux; }
     sp<IFilter> getFilterById(uint64_t filterId) { return mFilters[filterId]; }
 
-    std::map<uint64_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
+    map<uint64_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
 
     AssertionResult openFilterInDemux(DemuxFilterType type, uint32_t bufferSize);
     AssertionResult getNewlyOpenedFilterId_64bit(uint64_t& filterId);
+    AssertionResult getSharedAvMemoryHandle(uint64_t filterId);
+    AssertionResult releaseShareAvHandle(uint64_t filterId);
     AssertionResult configFilter(DemuxFilterSettings setting, uint64_t filterId);
     AssertionResult configIpFilterCid(uint32_t ipCid, uint64_t filterId);
     AssertionResult getFilterMQDescriptor(uint64_t filterId);
@@ -193,12 +211,14 @@
     sp<ITuner> mService;
     sp<IFilter> mFilter;
     sp<IDemux> mDemux;
-    std::map<uint64_t, sp<IFilter>> mFilters;
-    std::map<uint64_t, sp<FilterCallback>> mFilterCallbacks;
+    map<uint64_t, sp<IFilter>> mFilters;
+    map<uint64_t, sp<FilterCallback>> mFilterCallbacks;
 
     sp<FilterCallback> mFilterCallback;
     MQDesc mFilterMQDescriptor;
     vector<uint64_t> mUsedFilterIds;
 
+    hidl_handle mAvSharedHandle = NULL;
+
     uint64_t mFilterId = -1;
 };
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
index e06de53..f87fa3c 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
@@ -18,6 +18,10 @@
 
 namespace {
 
+AssertionResult TunerBroadcastHidlTest::filterDataOutputTest() {
+    return filterDataOutputTestBase(mFilterTests);
+}
+
 void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf,
                                                         FrontendConfig frontendConf) {
     uint32_t feId;
@@ -46,6 +50,38 @@
     ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
 
+void TunerBroadcastHidlTest::mediaFilterUsingSharedMemoryTest(FilterConfig filterConf,
+                                                              FrontendConfig frontendConf) {
+    uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    uint64_t filterId;
+
+    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+    ASSERT_TRUE(feId != INVALID_ID);
+    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    mFrontendTests.setDemux(demux);
+    mFilterTests.setDemux(demux);
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+    ASSERT_TRUE(mFilterTests.getSharedAvMemoryHandle(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    // tune test
+    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    ASSERT_TRUE(filterDataOutputTest());
+    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.releaseShareAvHandle(filterId));
+    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
 void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
                                                  FrontendConfig frontendConf, DvrConfig dvrConf) {
     uint32_t feId;
@@ -116,6 +152,16 @@
     mFrontendTests.scanTest(frontendScanArray[SCAN_DVBT], FrontendScanType::SCAN_BLIND);
 }
 
+TEST_P(TunerBroadcastHidlTest, MediaFilterWithSharedMemoryHandle) {
+    description("Test the Media Filter with shared memory handle");
+    mediaFilterUsingSharedMemoryTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, TunerBroadcastHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
+        android::hardware::PrintInstanceNameToString);
+
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, TunerFrontendHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
index 47004f6..f77a740 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
@@ -26,6 +26,20 @@
     initDvrConfig();
 }
 
+static AssertionResult success() {
+    return ::testing::AssertionSuccess();
+}
+
+AssertionResult filterDataOutputTestBase(FilterTests tests) {
+    // Data Verify Module
+    std::map<uint64_t, sp<FilterCallback>>::iterator it;
+    std::map<uint64_t, sp<FilterCallback>> filterCallbacks = tests.getFilterCallbacks();
+    for (it = filterCallbacks.begin(); it != filterCallbacks.end(); it++) {
+        it->second->testFilterDataOutput();
+    }
+    return success();
+}
+
 class TunerFilterHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
@@ -103,4 +117,34 @@
 };
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerFrontendHidlTest);
+
+class TunerBroadcastHidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        mService = ITuner::getService(GetParam());
+        ASSERT_NE(mService, nullptr);
+        initConfiguration();
+
+        mFrontendTests.setService(mService);
+        mDemuxTests.setService(mService);
+        mFilterTests.setService(mService);
+    }
+
+  protected:
+    static void description(const std::string& description) {
+        RecordProperty("description", description);
+    }
+
+    sp<ITuner> mService;
+    FrontendTests mFrontendTests;
+    DemuxTests mDemuxTests;
+    FilterTests mFilterTests;
+
+    AssertionResult filterDataOutputTest();
+
+    void mediaFilterUsingSharedMemoryTest(FilterConfig filterConf, FrontendConfig frontendConf);
+};
+
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerBroadcastHidlTest);
 }  // namespace
\ No newline at end of file