Copy filtered av data to ion buffer to test on cuttlefish

Test: atest VtsHalTvTunerV1_0TargetTest
Bug: 150952766
Change-Id: If007f9c021102dc95be8e9dc70be70d3945192a9
diff --git a/tv/tuner/1.0/default/Android.bp b/tv/tuner/1.0/default/Android.bp
index 989e25c..5711889 100644
--- a/tv/tuner/1.0/default/Android.bp
+++ b/tv/tuner/1.0/default/Android.bp
@@ -24,6 +24,7 @@
         "libfmq",
         "libhidlbase",
         "libhidlmemory",
+        "libion",
         "liblog",
         "libstagefright_foundation",
         "libutils",
diff --git a/tv/tuner/1.0/default/Filter.cpp b/tv/tuner/1.0/default/Filter.cpp
index 54d0952..f610c60 100644
--- a/tv/tuner/1.0/default/Filter.cpp
+++ b/tv/tuner/1.0/default/Filter.cpp
@@ -60,6 +60,8 @@
 Return<void> Filter::getQueueDesc(getQueueDesc_cb _hidl_cb) {
     ALOGV("%s", __FUNCTION__);
 
+    mIsUsingFMQ = true;
+
     _hidl_cb(Result::SUCCESS, *mFilterMQ->getDesc());
     return Void();
 }
@@ -120,9 +122,13 @@
     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 (mDataId2Avfd.find(avDataId) == mDataId2Avfd.end()) {
+        return Result::INVALID_ARGUMENT;
+    }
 
+    ::close(mDataId2Avfd[avDataId]);
     return Result::SUCCESS;
 }
 
@@ -174,14 +180,21 @@
     // Event Callback without waiting for the DATA_CONSUMED to init the process.
     while (mFilterThreadRunning) {
         if (mFilterEvent.events.size() == 0) {
-            ALOGD("[Filter] wait for filter data output.");
+            if (DEBUG_FILTER) {
+                ALOGD("[Filter] wait for filter data output.");
+            }
             usleep(1000 * 1000);
             continue;
         }
         // After successfully write, send a callback and wait for the read to be done
         mCallback->onFilterEvent(mFilterEvent);
+        freeAvHandle();
         mFilterEvent.events.resize(0);
         mFilterStatus = DemuxFilterStatus::DATA_READY;
+        if (mCallback == nullptr) {
+            ALOGD("[Filter] filter %d does not hava callback. Ending thread", mFilterId);
+            break;
+        }
         mCallback->onFilterStatus(mFilterStatus);
         break;
     }
@@ -191,7 +204,7 @@
         // We do not wait for the last round of written data to be read to finish the thread
         // because the VTS can verify the reading itself.
         for (int i = 0; i < SECTION_WRITE_COUNT; i++) {
-            while (mFilterThreadRunning) {
+            while (mFilterThreadRunning && mIsUsingFMQ) {
                 status_t status = mFilterEventFlag->wait(
                         static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED), &efState,
                         WAIT_TIMEOUT, true /* retry on spurious wake */);
@@ -202,11 +215,6 @@
                 break;
             }
 
-            if (mCallback == nullptr) {
-                ALOGD("[Filter] filter %d does not hava callback. Ending thread", mFilterId);
-                break;
-            }
-
             maySendFilterStatusCallback();
 
             while (mFilterThreadRunning) {
@@ -232,7 +240,22 @@
     ALOGD("[Filter] filter thread ended.");
 }
 
+void Filter::freeAvHandle() {
+    if (mType.mainType != DemuxFilterMainType::TS ||
+        (mType.subType.tsFilterType() == DemuxTsFilterType::AUDIO &&
+         mType.subType.tsFilterType() == DemuxTsFilterType::VIDEO)) {
+        return;
+    }
+    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());
+    }
+}
+
 void Filter::maySendFilterStatusCallback() {
+    if (!mIsUsingFMQ) {
+        return;
+    }
     std::lock_guard<std::mutex> lock(mFilterStatusLock);
     int availableToRead = mFilterMQ->availableToRead();
     int availableToWrite = mFilterMQ->availableToWrite();
@@ -409,19 +432,88 @@
 }
 
 Result Filter::startMediaFilterHandler() {
-    DemuxFilterMediaEvent mediaEvent;
-    mediaEvent = {
-            // temp dump meta data
-            .pts = 0,
-            .dataLength = 530,
-            .avMemory = nullptr,
-            .isSecureMemory = false,
-    };
-    mFilterEvent.events.resize(1);
-    mFilterEvent.events[0].media(mediaEvent);
+    std::lock_guard<std::mutex> lock(mFilterEventLock);
+    if (mFilterOutput.empty()) {
+        return Result::SUCCESS;
+    }
+
+    for (int i = 0; i < mFilterOutput.size(); i += 188) {
+        if (mPesSizeLeft == 0) {
+            uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
+                              mFilterOutput[i + 6];
+            if (DEBUG_FILTER) {
+                ALOGD("[Filter] prefix %d", prefix);
+            }
+            if (prefix == 0x000001) {
+                // TODO handle mulptiple Pes filters
+                mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9];
+                mPesSizeLeft += 6;
+                if (DEBUG_FILTER) {
+                    ALOGD("[Filter] pes data length %d", mPesSizeLeft);
+                }
+            } else {
+                continue;
+            }
+        }
+
+        int endPoint = min(184, mPesSizeLeft);
+        // append data and check size
+        vector<uint8_t>::const_iterator first = mFilterOutput.begin() + i + 4;
+        vector<uint8_t>::const_iterator last = mFilterOutput.begin() + i + 4 + endPoint;
+        mPesOutput.insert(mPesOutput.end(), first, last);
+        // size does not match then continue
+        mPesSizeLeft -= endPoint;
+        if (DEBUG_FILTER) {
+            ALOGD("[Filter] pes data left %d", mPesSizeLeft);
+        }
+        if (mPesSizeLeft > 0 || mAvBufferCopyCount++ < 10) {
+            continue;
+        }
+
+        int av_fd = createAvIonFd(mPesOutput.size());
+        if (av_fd == -1) {
+            return Result::UNKNOWN_ERROR;
+        }
+        // copy the filtered data to the buffer
+        uint8_t* avBuffer = getIonBuffer(av_fd, mPesOutput.size());
+        if (avBuffer == NULL) {
+            return Result::UNKNOWN_ERROR;
+        }
+        memcpy(avBuffer, mPesOutput.data(), mPesOutput.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>(mPesOutput.size()),
+                .avDataId = dataId,
+        };
+        int size = mFilterEvent.events.size();
+        mFilterEvent.events.resize(size + 1);
+        mFilterEvent.events[size].media(mediaEvent);
+
+        // Clear and log
+        mPesOutput.clear();
+        mAvBufferCopyCount = 0;
+        ::close(av_fd);
+        if (DEBUG_FILTER) {
+            ALOGD("[Filter] assembled av data length %d", mediaEvent.dataLength);
+        }
+    }
 
     mFilterOutput.clear();
-    // TODO handle write FQM for media stream
+
     return Result::SUCCESS;
 }
 
@@ -493,6 +585,42 @@
     mDvr = nullptr;
 }
 
+int Filter::createAvIonFd(int size) {
+    // Create an ion fd and allocate an av fd mapped to a buffer to it.
+    int ion_fd = ion_open();
+    if (ion_fd == -1) {
+        ALOGE("[Filter] Failed to open ion fd %d", errno);
+        return -1;
+    }
+    int av_fd = -1;
+    ion_alloc_fd(dup(ion_fd), size, 0 /*align*/, ION_HEAP_SYSTEM_MASK, 0 /*flags*/, &av_fd);
+    if (av_fd == -1) {
+        ALOGE("[Filter] Failed to create av fd %d", errno);
+        return -1;
+    }
+    return av_fd;
+}
+
+uint8_t* Filter::getIonBuffer(int fd, int size) {
+    uint8_t* avBuf = static_cast<uint8_t*>(
+            mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 /*offset*/));
+    if (avBuf == MAP_FAILED) {
+        ALOGE("[Filter] fail to allocate buffer %d", errno);
+        return NULL;
+    }
+    return avBuf;
+}
+
+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);
+    if (nativeHandle == NULL) {
+        ALOGE("[Filter] Failed to create native_handle %d", errno);
+        return NULL;
+    }
+    nativeHandle->data[0] = dup(fd);
+    return nativeHandle;
+}
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace tuner
diff --git a/tv/tuner/1.0/default/Filter.h b/tv/tuner/1.0/default/Filter.h
index 0dc992a..afed98e 100644
--- a/tv/tuner/1.0/default/Filter.h
+++ b/tv/tuner/1.0/default/Filter.h
@@ -19,6 +19,7 @@
 
 #include <android/hardware/tv/tuner/1.0/IFilter.h>
 #include <fmq/MessageQueue.h>
+#include <ion/ion.h>
 #include <math.h>
 #include <set>
 #include "Demux.h"
@@ -87,6 +88,7 @@
     Result startRecordFilterHandler();
     void attachFilterToRecord(const sp<Dvr> dvr);
     void detachFilterFromRecord();
+    void freeAvHandle();
 
   private:
     // Tuner service
@@ -109,6 +111,7 @@
     vector<uint8_t> mFilterOutput;
     vector<uint8_t> mRecordFilterOutput;
     unique_ptr<FilterMQ> mFilterMQ;
+    bool mIsUsingFMQ = false;
     EventFlag* mFilterEventFlag;
     DemuxFilterEvent mFilterEvent;
 
@@ -160,6 +163,10 @@
     static void* __threadLoopFilter(void* user);
     void filterThreadLoop();
 
+    int createAvIonFd(int size);
+    uint8_t* getIonBuffer(int fd, int size);
+    native_handle_t* createNativeHandle(int fd);
+
     /**
      * Lock to protect writes to the FMQs
      */
@@ -181,6 +188,11 @@
     // TODO handle mulptiple Pes filters
     int mPesSizeLeft = 0;
     vector<uint8_t> mPesOutput;
+
+    // A map from data id to ion handle
+    std::map<uint64_t, int> mDataId2Avfd;
+    uint64_t mLastUsedDataId = 1;
+    int mAvBufferCopyCount = 0;
 };
 
 }  // namespace implementation
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index 7e206a7..bb0d8dc 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -63,9 +63,6 @@
         return Result::INVALID_STATE;
     }
 
-    // TODO dynamically allocate file to the source file
-    mSourceStreamFile = FRONTEND_STREAM_FILE;
-
     mCallback->onEvent(FrontendEventType::LOCKED);
     return Result::SUCCESS;
 }
@@ -180,7 +177,7 @@
 }
 
 string Frontend::getSourceFile() {
-    return mSourceStreamFile;
+    return FRONTEND_STREAM_FILE;
 }
 
 }  // namespace implementation
diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h
index eab43a3..b954639 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -76,7 +76,6 @@
     FrontendId mId = 0;
 
     const string FRONTEND_STREAM_FILE = "/vendor/etc/dumpTs3.ts";
-    string mSourceStreamFile;
     std::ifstream mFrontendData;
 };
 
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 4fd3355..8fb5061 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -106,7 +106,7 @@
     return Void();
 }
 
-Return<void> Tuner::getFrontendInfo(FrontendId /* frontendId */, getFrontendInfo_cb _hidl_cb) {
+Return<void> Tuner::getFrontendInfo(FrontendId /*frontendId*/, getFrontendInfo_cb _hidl_cb) {
     ALOGV("%s", __FUNCTION__);
 
     vector<FrontendStatusType> statusCaps = {
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index f693e7c..bfc077f 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -57,6 +57,7 @@
 using android::sp;
 using android::hardware::EventFlag;
 using android::hardware::fromHeap;
+using android::hardware::hidl_handle;
 using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 using android::hardware::HidlMemory;
@@ -68,6 +69,7 @@
 using android::hardware::tv::tuner::V1_0::DataFormat;
 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::DemuxFilterPesDataSettings;
 using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
 using android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
@@ -313,6 +315,7 @@
     }
 
     void setFilterId(uint32_t filterId) { mFilterId = filterId; }
+    void setFilterInterface(sp<IFilter> filter) { mFilter = filter; }
     void setFilterEventType(FilterEventType type) { mFilterEventType = type; }
 
     void testFilterDataOutput();
@@ -324,6 +327,7 @@
     void updateFilterMQ(MQDesc& filterMQDescriptor);
     void updateGoldenOutputMap(string goldenOutputFile);
     bool readFilterEventData();
+    bool dumpAvData(DemuxFilterMediaEvent event);
 
   private:
     struct FilterThreadArgs {
@@ -336,6 +340,7 @@
     string mFilterIdToGoldenOutput;
 
     uint32_t mFilterId;
+    sp<IFilter> mFilter;
     FilterEventType mFilterEventType;
     std::unique_ptr<FilterMQ> mFilterMQ;
     EventFlag* mFilterMQEventFlag;
@@ -407,7 +412,7 @@
 bool FilterCallback::readFilterEventData() {
     bool result = false;
     DemuxFilterEvent filterEvent = mFilterEvent;
-    ALOGW("[vts] reading from filter FMQ %d", mFilterId);
+    ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId);
     // todo separate filter handlers
     for (int i = 0; i < filterEvent.events.size(); i++) {
         switch (mFilterEventType) {
@@ -418,8 +423,7 @@
                 mDataLength = filterEvent.events[i].pes().dataLength;
                 break;
             case FilterEventType::MEDIA:
-                mDataLength = filterEvent.events[i].media().dataLength;
-                break;
+                return dumpAvData(filterEvent.events[i].media());
             case FilterEventType::RECORD:
                 break;
             case FilterEventType::MMTPRECORD:
@@ -443,6 +447,26 @@
     mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
     return result;
 }
+
+bool FilterCallback::dumpAvData(DemuxFilterMediaEvent event) {
+    uint32_t length = event.dataLength;
+    uint64_t dataId = event.avDataId;
+    // read data from buffer pointed by a handle
+    hidl_handle handle = event.avMemory;
+
+    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 /*offset*/));
+    if (buffer == MAP_FAILED) {
+        ALOGE("[vts] fail to allocate av buffer, errno=%d", errno);
+        return false;
+    }
+    uint8_t output[length + 1];
+    memcpy(output, buffer, length);
+    // print buffer and check with golden output.
+    EXPECT_TRUE(mFilter->releaseAvHandle(handle, dataId) == Result::SUCCESS);
+    return true;
+}
 /******************************** End FilterCallback **********************************/
 
 /******************************** Start DvrCallback **********************************/
@@ -731,6 +755,7 @@
     sp<IFilter> mFilter;
     std::map<uint32_t, sp<IFilter>> mFilters;
     std::map<uint32_t, sp<FilterCallback>> mFilterCallbacks;
+
     sp<FilterCallback> mFilterCallback;
     sp<DvrCallback> mDvrCallback;
     MQDesc mFilterMQDescriptor;
@@ -926,6 +951,7 @@
 
     if (status == Result::SUCCESS) {
         mFilterCallback->setFilterId(mFilterId);
+        mFilterCallback->setFilterInterface(mFilter);
         mUsedFilterIds.insert(mUsedFilterIds.end(), mFilterId);
         mFilters[mFilterId] = mFilter;
         mFilterCallbacks[mFilterId] = mFilterCallback;
@@ -1535,6 +1561,39 @@
 
     ASSERT_TRUE(recordDataFlowTest(filterConf, recordSetting, goldenOutputFiles));
 }*/
+
+TEST_P(TunerHidlTest, AvBufferTest) {
+    description("Test the av filter data bufferring.");
+
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
+
+    for (size_t i = 0; i < mFeIds.size(); i++) {
+        ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
+        if (mFrontendInfo.type != frontendArray[1].type) {
+            continue;
+        }
+        ASSERT_TRUE(openFrontend(mFeIds[i]));
+        ASSERT_TRUE(setFrontendCallback());
+        ASSERT_TRUE(openDemux());
+        ASSERT_TRUE(openFilterInDemux(filterArray[0].type));
+        uint32_t filterId;
+        ASSERT_TRUE(getNewlyOpenedFilterId(filterId));
+        ASSERT_TRUE(configFilter(filterArray[0].setting, filterId));
+        ASSERT_TRUE(startFilter(filterId));
+        ASSERT_TRUE(setDemuxFrontendDataSource(mFeIds[i]));
+        // tune test
+        ASSERT_TRUE(tuneFrontend(frontendArray[1]));
+        // broadcast data flow test
+        ASSERT_TRUE(broadcastDataFlowTest(goldenOutputFiles));
+        ASSERT_TRUE(stopTuneFrontend());
+        ASSERT_TRUE(stopFilter(filterId));
+        ASSERT_TRUE(closeFilter(filterId));
+        ASSERT_TRUE(closeDemux());
+        ASSERT_TRUE(closeFrontend());
+        break;
+    }
+}
 /*============================== End Data Flow Tests ==============================*/
 /******************************** End Test Entry **********************************/
 }  // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index 55ca857..25612d7 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -64,7 +64,7 @@
 
 namespace {
 
-#define frontend_transponders_count 1
+#define frontend_transponders_count 2
 #define channels_count 1
 #define frontend_scan_count 1
 #define filter_count 2
@@ -108,6 +108,7 @@
             .standard = FrontendDvbtStandard::T,
     };
     frontendArray[0].type = FrontendType::DVBT, frontendArray[0].settings.dvbt(dvbtSettings);
+    frontendArray[1].type = FrontendType::DVBS;
 };
 
 /** Configuration array for the frontend scan test */
@@ -122,7 +123,7 @@
     // TS Video filter setting
     filterArray[0].type.mainType = DemuxFilterMainType::TS;
     filterArray[0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
-    filterArray[0].setting.ts().tpid = 49;
+    filterArray[0].setting.ts().tpid = 119;
     filterArray[0].setting.ts().filterSettings.av({.isPassthrough = false});
     // TS PES filter setting
     filterArray[1].type.mainType = DemuxFilterMainType::TS;