Add VTS for Filter TimeDelayHint

Test: atest VtsHalTvTunerTargetTest
Bug: 183057734
Change-Id: I4a97d81c0100ca4114353ed84335fc1593bff800
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.cpp b/tv/tuner/aidl/vts/functional/FilterTests.cpp
index c53adb2..a5acdc1 100644
--- a/tv/tuner/aidl/vts/functional/FilterTests.cpp
+++ b/tv/tuner/aidl/vts/functional/FilterTests.cpp
@@ -23,6 +23,33 @@
 
 using ::aidl::android::hardware::common::NativeHandle;
 
+::ndk::ScopedAStatus FilterCallback::onFilterEvent(const vector<DemuxFilterEvent>& events) {
+    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
+    readFilterEventsData(events);
+    mPidFilterOutputCount++;
+    mMsgCondition.signal();
+
+    // HACK: we need to cast the const away as DemuxFilterEvent contains a ScopedFileDescriptor
+    // that cannot be copied.
+    for (auto&& e : const_cast<std::vector<DemuxFilterEvent>&>(events)) {
+        auto it = mFilterEventPromises.find(e.getTag());
+        if (it != mFilterEventPromises.cend()) {
+            it->second.set_value(std::move(e));
+            mFilterEventPromises.erase(it);
+        }
+    }
+
+    return ::ndk::ScopedAStatus::ok();
+}
+
+std::future<DemuxFilterEvent> FilterCallback::getNextFilterEventWithTag(DemuxFilterEvent::Tag tag) {
+    // Note: this currently only supports one future per DemuxFilterEvent::Tag.
+    mFilterEventPromises[tag] = std::promise<DemuxFilterEvent>();
+    return mFilterEventPromises[tag].get_future();
+}
+
 void FilterCallback::testFilterDataOutput() {
     android::Mutex::Autolock autoLock(mMsgLock);
     while (mPidFilterOutputCount < 1) {
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.h b/tv/tuner/aidl/vts/functional/FilterTests.h
index c965d95..6258bac 100644
--- a/tv/tuner/aidl/vts/functional/FilterTests.h
+++ b/tv/tuner/aidl/vts/functional/FilterTests.h
@@ -24,7 +24,9 @@
 #include <log/log.h>
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
+#include <future>
 #include <map>
+#include <unordered_map>
 
 #include <fmq/AidlMessageQueue.h>
 
@@ -58,15 +60,9 @@
 
 class FilterCallback : public BnFilterCallback {
   public:
-    virtual ::ndk::ScopedAStatus onFilterEvent(const vector<DemuxFilterEvent>& events) 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
-        readFilterEventsData(events);
-        mPidFilterOutputCount++;
-        mMsgCondition.signal();
-        return ::ndk::ScopedAStatus::ok();
-    }
+    virtual ::ndk::ScopedAStatus onFilterEvent(const vector<DemuxFilterEvent>& events) override;
+
+    std::future<DemuxFilterEvent> getNextFilterEventWithTag(DemuxFilterEvent::Tag tag);
 
     virtual ::ndk::ScopedAStatus onFilterStatus(const DemuxFilterStatus /*status*/) override {
         return ::ndk::ScopedAStatus::ok();
@@ -89,6 +85,7 @@
     int32_t mFilterId;
     std::shared_ptr<IFilter> mFilter;
 
+    std::unordered_map<DemuxFilterEvent::Tag, std::promise<DemuxFilterEvent>> mFilterEventPromises;
     native_handle_t* mAvSharedHandle = nullptr;
     uint64_t mAvSharedMemSize = -1;
 
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
index 85e5c68..88890e4 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -640,6 +640,57 @@
     testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
 }
 
+// TODO: move boilerplate into text fixture
+TEST_P(TunerFilterAidlTest, FilterTimeDelayHintTest) {
+    description("Test filter delay hint.");
+
+    int32_t feId;
+    int32_t demuxId;
+    std::shared_ptr<IDemux> demux;
+    int64_t filterId;
+
+    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].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));
+    mFilterTests.setDemux(demux);
+
+    const auto& filterConf = filterMap[live.ipFilterId];
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.configIpFilterCid(filterConf.ipCid, filterId));
+
+    auto filter = mFilterTests.getFilterById(filterId);
+
+    auto delayValue = std::chrono::milliseconds(5000);
+    FilterDelayHint delayHint;
+    delayHint.hintType = FilterDelayHintType::TIME_DELAY_IN_MS;
+    delayHint.hintValue = delayValue.count();
+
+    auto status = filter->setDelayHint(delayHint);
+    ASSERT_TRUE(status.isOk());
+
+    auto startTime = std::chrono::steady_clock::now();
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+
+    auto cb = mFilterTests.getFilterCallbacks().at(filterId);
+    auto future = cb->getNextFilterEventWithTag(DemuxFilterEvent::Tag::ipPayload);
+
+    // block and wait for callback to be received.
+    ASSERT_EQ(future.wait_for(std::chrono::seconds(10)), std::future_status::ready);
+    auto duration = std::chrono::steady_clock::now() - startTime;
+    ASSERT_GE(duration, delayValue);
+
+    // cleanup
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
 TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsSectionFilterTest) {
     description("Feed ts data from playback and configure Ts section filter to get output");
     if (!playback.support || playback.sectionFilterId.compare(emptyHardwareId) == 0) {