Merge "Make dvr tests a separate module" into rvc-dev
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index 43c4e3a..95b4ebc 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -52,7 +52,7 @@
 
     mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
 
-    return startFrontendInputLoop();
+    return Result::SUCCESS;
 }
 
 Return<void> Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize,
@@ -60,7 +60,6 @@
     ALOGV("%s", __FUNCTION__);
 
     uint32_t filterId;
-
     if (!mUnusedFilterIds.empty()) {
         filterId = *mUnusedFilterIds.begin();
 
@@ -83,7 +82,6 @@
         _hidl_cb(Result::UNKNOWN_ERROR, filter);
         return Void();
     }
-
     mFilters[filterId] = filter;
 
     _hidl_cb(Result::SUCCESS, filter);
@@ -234,11 +232,9 @@
     return mFilters[filterId]->getTpid();
 }
 
-Result Demux::startFrontendInputLoop() {
+void Demux::startFrontendInputLoop() {
     pthread_create(&mFrontendInputThread, NULL, __threadLoopFrontend, this);
     pthread_setname_np(mFrontendInputThread, "frontend_input_thread");
-
-    return Result::SUCCESS;
 }
 
 void* Demux::__threadLoopFrontend(void* user) {
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 1405d0c..759e348 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -89,6 +89,7 @@
     void updateFilterOutput(uint16_t filterId, vector<uint8_t> data);
     uint16_t getFilterTpid(uint32_t filterId);
     void setIsRecording(bool isRecording);
+    void startFrontendInputLoop();
 
   private:
     // Tuner service
@@ -104,7 +105,6 @@
         uint32_t filterId;
     };
 
-    Result startFrontendInputLoop();
     static void* __threadLoopFrontend(void* user);
     void frontendInputThreadLoop();
 
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index ae5f587..b509599 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -64,6 +64,7 @@
         return Result::INVALID_STATE;
     }
 
+    mTunerService->frontendStartTune(mId);
     mCallback->onEvent(FrontendEventType::LOCKED);
     mIsLocked = false;
     return Result::SUCCESS;
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 27a67bf..b1f2490 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -247,6 +247,15 @@
     }
 }
 
+void Tuner::frontendStartTune(uint32_t frontendId) {
+    map<uint32_t, uint32_t>::iterator it = mFrontendToDemux.find(frontendId);
+    uint32_t demuxId;
+    if (it != mFrontendToDemux.end()) {
+        demuxId = it->second;
+        mDemuxes[demuxId]->startFrontendInputLoop();
+    }
+}
+
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace tuner
diff --git a/tv/tuner/1.0/default/Tuner.h b/tv/tuner/1.0/default/Tuner.h
index 3f2d60d..5de568f 100644
--- a/tv/tuner/1.0/default/Tuner.h
+++ b/tv/tuner/1.0/default/Tuner.h
@@ -63,6 +63,7 @@
 
     void setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId);
 
+    void frontendStartTune(uint32_t frontendId);
     void frontendStopTune(uint32_t frontendId);
 
   private:
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index 448575e..b152a29 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -22,6 +22,7 @@
         "FrontendTests.cpp",
         "DemuxTests.cpp",
         "FilterTests.cpp",
+        "DvrTests.cpp",
     ],
     static_libs: [
         "android.hardware.tv.tuner@1.0",
diff --git a/tv/tuner/1.0/vts/functional/DemuxTests.h b/tv/tuner/1.0/vts/functional/DemuxTests.h
index a72c09f..6e1e395 100644
--- a/tv/tuner/1.0/vts/functional/DemuxTests.h
+++ b/tv/tuner/1.0/vts/functional/DemuxTests.h
@@ -38,8 +38,6 @@
 
 class DemuxTests {
   public:
-    sp<ITuner> mService;
-
     void setService(sp<ITuner> tuner) { mService = tuner; }
 
     AssertionResult openDemux(sp<IDemux>& demux, uint32_t& demuxId);
@@ -51,5 +49,6 @@
 
     static AssertionResult success() { return ::testing::AssertionSuccess(); }
 
+    sp<ITuner> mService;
     sp<IDemux> mDemux;
 };
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.cpp b/tv/tuner/1.0/vts/functional/DvrTests.cpp
new file mode 100644
index 0000000..a1ce23d
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/DvrTests.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#include "DvrTests.h"
+
+void DvrCallback::startPlaybackInputThread(PlaybackConf playbackConf,
+                                           MQDesc& playbackMQDescriptor) {
+    mPlaybackMQ = std::make_unique<FilterMQ>(playbackMQDescriptor, true /* resetPointers */);
+    EXPECT_TRUE(mPlaybackMQ);
+    struct PlaybackThreadArgs* threadArgs =
+            (struct PlaybackThreadArgs*)malloc(sizeof(struct PlaybackThreadArgs));
+    threadArgs->user = this;
+    threadArgs->playbackConf = &playbackConf;
+    threadArgs->keepWritingPlaybackFMQ = &mKeepWritingPlaybackFMQ;
+
+    pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, (void*)threadArgs);
+    pthread_setname_np(mPlaybackThread, "test_playback_input_loop");
+}
+
+void DvrCallback::stopPlaybackThread() {
+    mPlaybackThreadRunning = false;
+    mKeepWritingPlaybackFMQ = false;
+
+    android::Mutex::Autolock autoLock(mPlaybackThreadLock);
+}
+
+void* DvrCallback::__threadLoopPlayback(void* threadArgs) {
+    DvrCallback* const self =
+            static_cast<DvrCallback*>(((struct PlaybackThreadArgs*)threadArgs)->user);
+    self->playbackThreadLoop(((struct PlaybackThreadArgs*)threadArgs)->playbackConf,
+                             ((struct PlaybackThreadArgs*)threadArgs)->keepWritingPlaybackFMQ);
+    return 0;
+}
+
+void DvrCallback::playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ) {
+    android::Mutex::Autolock autoLock(mPlaybackThreadLock);
+    mPlaybackThreadRunning = true;
+
+    // Create the EventFlag that is used to signal the HAL impl that data have been
+    // written into the Playback FMQ
+    EventFlag* playbackMQEventFlag;
+    EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
+                android::OK);
+
+    // open the stream and get its length
+    std::ifstream inputData(playbackConf->inputDataFile, std::ifstream::binary);
+    int writeSize = playbackConf->setting.packetSize * 6;
+    char* buffer = new char[writeSize];
+    ALOGW("[vts] playback thread loop start %s", playbackConf->inputDataFile.c_str());
+    if (!inputData.is_open()) {
+        mPlaybackThreadRunning = false;
+        ALOGW("[vts] Error %s", strerror(errno));
+    }
+
+    while (mPlaybackThreadRunning) {
+        // move the stream pointer for packet size * 6 every read until the end
+        while (*keepWritingPlaybackFMQ) {
+            inputData.read(buffer, writeSize);
+            if (!inputData) {
+                int leftSize = inputData.gcount();
+                if (leftSize == 0) {
+                    mPlaybackThreadRunning = false;
+                    break;
+                }
+                inputData.clear();
+                inputData.read(buffer, leftSize);
+                // Write the left over of the input data and quit the thread
+                if (leftSize > 0) {
+                    EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], leftSize));
+                    playbackMQEventFlag->wake(
+                            static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+                }
+                mPlaybackThreadRunning = false;
+                break;
+            }
+            // Write input FMQ and notify the Tuner Implementation
+            EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], writeSize));
+            playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+            inputData.seekg(writeSize, inputData.cur);
+            sleep(1);
+        }
+    }
+
+    ALOGW("[vts] Playback thread end.");
+
+    delete[] buffer;
+    inputData.close();
+}
+
+void DvrCallback::testRecordOutput() {
+    android::Mutex::Autolock autoLock(mMsgLock);
+    while (mDataOutputBuffer.empty()) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            EXPECT_TRUE(false) << "record output matching pid does not output within timeout";
+            return;
+        }
+    }
+    stopRecordThread();
+    ALOGW("[vts] record pass and stop");
+}
+
+void DvrCallback::startRecordOutputThread(RecordSettings recordSettings,
+                                          MQDesc& recordMQDescriptor) {
+    mRecordMQ = std::make_unique<FilterMQ>(recordMQDescriptor, true /* resetPointers */);
+    EXPECT_TRUE(mRecordMQ);
+    struct RecordThreadArgs* threadArgs =
+            (struct RecordThreadArgs*)malloc(sizeof(struct RecordThreadArgs));
+    threadArgs->user = this;
+    threadArgs->recordSettings = &recordSettings;
+    threadArgs->keepReadingRecordFMQ = &mKeepReadingRecordFMQ;
+
+    pthread_create(&mRecordThread, NULL, __threadLoopRecord, (void*)threadArgs);
+    pthread_setname_np(mRecordThread, "test_record_input_loop");
+}
+
+void* DvrCallback::__threadLoopRecord(void* threadArgs) {
+    DvrCallback* const self =
+            static_cast<DvrCallback*>(((struct RecordThreadArgs*)threadArgs)->user);
+    self->recordThreadLoop(((struct RecordThreadArgs*)threadArgs)->recordSettings,
+                           ((struct RecordThreadArgs*)threadArgs)->keepReadingRecordFMQ);
+    return 0;
+}
+
+void DvrCallback::recordThreadLoop(RecordSettings* /*recordSettings*/, bool* keepReadingRecordFMQ) {
+    ALOGD("[vts] DvrCallback record threadLoop start.");
+    android::Mutex::Autolock autoLock(mRecordThreadLock);
+    mRecordThreadRunning = true;
+
+    // Create the EventFlag that is used to signal the HAL impl that data have been
+    // read from the Record FMQ
+    EventFlag* recordMQEventFlag;
+    EXPECT_TRUE(EventFlag::createEventFlag(mRecordMQ->getEventFlagWord(), &recordMQEventFlag) ==
+                android::OK);
+
+    while (mRecordThreadRunning) {
+        while (*keepReadingRecordFMQ) {
+            uint32_t efState = 0;
+            android::status_t status = recordMQEventFlag->wait(
+                    static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT,
+                    true /* retry on spurious wake */);
+            if (status != android::OK) {
+                ALOGD("[vts] wait for data ready on the record FMQ");
+                continue;
+            }
+            // Our current implementation filter the data and write it into the filter FMQ
+            // immediately after the DATA_READY from the VTS/framework
+            if (!readRecordFMQ()) {
+                ALOGD("[vts] record data failed to be filtered. Ending thread");
+                mRecordThreadRunning = false;
+                break;
+            }
+        }
+    }
+
+    mRecordThreadRunning = false;
+    ALOGD("[vts] record thread ended.");
+}
+
+bool DvrCallback::readRecordFMQ() {
+    android::Mutex::Autolock autoLock(mMsgLock);
+    bool result = false;
+    mDataOutputBuffer.clear();
+    mDataOutputBuffer.resize(mRecordMQ->availableToRead());
+    result = mRecordMQ->read(mDataOutputBuffer.data(), mRecordMQ->availableToRead());
+    EXPECT_TRUE(result) << "can't read from Record MQ";
+    mMsgCondition.signal();
+    return result;
+}
+
+void DvrCallback::stopRecordThread() {
+    mKeepReadingRecordFMQ = false;
+    mRecordThreadRunning = false;
+    android::Mutex::Autolock autoLock(mRecordThreadLock);
+}
+
+AssertionResult DvrTests::openDvrInDemux(DvrType type) {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+
+    // Create dvr callback
+    mDvrCallback = new DvrCallback();
+
+    mDemux->openDvr(type, FMQ_SIZE_1M, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
+        mDvr = dvr;
+        status = result;
+    });
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::configDvr(DvrSettings setting) {
+    Result status = mDvr->configure(setting);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::getDvrMQDescriptor() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
+        mDvrMQDescriptor = dvrMQDesc;
+        status = result;
+    });
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::attachFilterToDvr(sp<IFilter> filter) {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    status = mDvr->attachFilter(filter);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::detachFilterToDvr(sp<IFilter> filter) {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    status = mDvr->detachFilter(filter);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::startDvr() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    status = mDvr->start();
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::stopDvr() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    status = mDvr->stop();
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+void DvrTests::closeDvr() {
+    ASSERT_TRUE(mDemux);
+    ASSERT_TRUE(mDvr);
+    ASSERT_TRUE(mDvr->close() == Result::SUCCESS);
+}
\ No newline at end of file
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.h b/tv/tuner/1.0/vts/functional/DvrTests.h
new file mode 100644
index 0000000..74ef58e
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/DvrTests.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/tv/tuner/1.0/IDvr.h>
+#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/Status.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <fstream>
+#include <iostream>
+#include <map>
+
+#include "FilterTests.h"
+
+using android::Condition;
+using android::Mutex;
+using android::sp;
+using android::hardware::EventFlag;
+using android::hardware::kSynchronizedReadWrite;
+using android::hardware::MessageQueue;
+using android::hardware::MQDescriptorSync;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
+using android::hardware::tv::tuner::V1_0::DvrSettings;
+using android::hardware::tv::tuner::V1_0::DvrType;
+using android::hardware::tv::tuner::V1_0::IDvr;
+using android::hardware::tv::tuner::V1_0::IDvrCallback;
+using android::hardware::tv::tuner::V1_0::ITuner;
+using android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using android::hardware::tv::tuner::V1_0::PlaybackStatus;
+using android::hardware::tv::tuner::V1_0::RecordSettings;
+using android::hardware::tv::tuner::V1_0::RecordStatus;
+using android::hardware::tv::tuner::V1_0::Result;
+
+#define WAIT_TIMEOUT 3000000000
+
+struct PlaybackConf {
+    string inputDataFile;
+    PlaybackSettings setting;
+};
+
+class DvrCallback : public IDvrCallback {
+  public:
+    virtual Return<void> onRecordStatus(DemuxFilterStatus status) override {
+        ALOGW("[vts] record status %hhu", status);
+        switch (status) {
+            case DemuxFilterStatus::DATA_READY:
+                break;
+            case DemuxFilterStatus::LOW_WATER:
+                break;
+            case DemuxFilterStatus::HIGH_WATER:
+            case DemuxFilterStatus::OVERFLOW:
+                ALOGW("[vts] record overflow. Flushing");
+                break;
+        }
+        return Void();
+    }
+
+    virtual Return<void> onPlaybackStatus(PlaybackStatus status) override {
+        // android::Mutex::Autolock autoLock(mMsgLock);
+        ALOGW("[vts] playback status %d", status);
+        switch (status) {
+            case PlaybackStatus::SPACE_EMPTY:
+            case PlaybackStatus::SPACE_ALMOST_EMPTY:
+                ALOGW("[vts] keep playback inputing %d", status);
+                mKeepWritingPlaybackFMQ = true;
+                break;
+            case PlaybackStatus::SPACE_ALMOST_FULL:
+            case PlaybackStatus::SPACE_FULL:
+                ALOGW("[vts] stop playback inputing %d", status);
+                mKeepWritingPlaybackFMQ = false;
+                break;
+        }
+        return Void();
+    }
+
+    void stopPlaybackThread();
+    void testRecordOutput();
+    void stopRecordThread();
+
+    void startPlaybackInputThread(PlaybackConf playbackConf, MQDesc& playbackMQDescriptor);
+    void startRecordOutputThread(RecordSettings recordSettings, MQDesc& recordMQDescriptor);
+    static void* __threadLoopPlayback(void* threadArgs);
+    static void* __threadLoopRecord(void* threadArgs);
+    void playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ);
+    void recordThreadLoop(RecordSettings* recordSetting, bool* keepWritingPlaybackFMQ);
+
+    bool readRecordFMQ();
+
+  private:
+    struct PlaybackThreadArgs {
+        DvrCallback* user;
+        PlaybackConf* playbackConf;
+        bool* keepWritingPlaybackFMQ;
+    };
+    struct RecordThreadArgs {
+        DvrCallback* user;
+        RecordSettings* recordSettings;
+        bool* keepReadingRecordFMQ;
+    };
+    // uint16_t mDataLength = 0;
+    std::vector<uint8_t> mDataOutputBuffer;
+
+    std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterMQ;
+    std::unique_ptr<FilterMQ> mPlaybackMQ;
+    std::unique_ptr<FilterMQ> mRecordMQ;
+    std::map<uint32_t, EventFlag*> mFilterMQEventFlag;
+
+    android::Mutex mMsgLock;
+    android::Mutex mPlaybackThreadLock;
+    android::Mutex mRecordThreadLock;
+    android::Condition mMsgCondition;
+
+    bool mKeepWritingPlaybackFMQ = true;
+    bool mKeepReadingRecordFMQ = true;
+    bool mPlaybackThreadRunning;
+    bool mRecordThreadRunning;
+    pthread_t mPlaybackThread;
+    pthread_t mRecordThread;
+
+    // int mPidFilterOutputCount = 0;
+};
+
+class DvrTests {
+  public:
+    void setService(sp<ITuner> tuner) { mService = tuner; }
+    void setDemux(sp<IDemux> demux) { mDemux = demux; }
+
+    void startPlaybackInputThread(string dataInputFile, PlaybackSettings settings) {
+        PlaybackConf conf{
+                .inputDataFile = dataInputFile,
+                .setting = settings,
+        };
+        mDvrCallback->startPlaybackInputThread(conf, mDvrMQDescriptor);
+    };
+
+    void startRecordOutputThread(RecordSettings settings) {
+        mDvrCallback->startRecordOutputThread(settings, mDvrMQDescriptor);
+    };
+
+    void stopPlaybackThread() { mDvrCallback->stopPlaybackThread(); }
+    void testRecordOutput() { mDvrCallback->testRecordOutput(); }
+    void stopRecordThread() { mDvrCallback->stopPlaybackThread(); }
+
+    AssertionResult openDvrInDemux(DvrType type);
+    AssertionResult configDvr(DvrSettings setting);
+    AssertionResult getDvrMQDescriptor();
+    AssertionResult attachFilterToDvr(sp<IFilter> filter);
+    AssertionResult detachFilterToDvr(sp<IFilter> filter);
+    AssertionResult stopDvr();
+    AssertionResult startDvr();
+    void closeDvr();
+
+  protected:
+    static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+    static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+    sp<ITuner> mService;
+    sp<IDvr> mDvr;
+    sp<IDemux> mDemux;
+    sp<DvrCallback> mDvrCallback;
+    MQDesc mDvrMQDescriptor;
+
+    pthread_t mPlaybackshread;
+    bool mPlaybackThreadRunning;
+};
\ No newline at end of file
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.h b/tv/tuner/1.0/vts/functional/FilterTests.h
index 3cc06e5..dc798c9 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.h
+++ b/tv/tuner/1.0/vts/functional/FilterTests.h
@@ -149,6 +149,7 @@
   public:
     void setService(sp<ITuner> tuner) { mService = tuner; }
     void setDemux(sp<IDemux> demux) { mDemux = demux; }
+    sp<IFilter> getFilterById(uint32_t filterId) { return mFilters[filterId]; }
 
     std::map<uint32_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
 
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index d836c26..f211be2 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -17,181 +17,8 @@
 #include "VtsHalTvTunerV1_0TargetTest.h"
 
 namespace {
-
-/******************************** Start DvrCallback **********************************/
-void DvrCallback::startPlaybackInputThread(PlaybackConf playbackConf,
-                                           MQDesc& playbackMQDescriptor) {
-    mPlaybackMQ = std::make_unique<FilterMQ>(playbackMQDescriptor, true /* resetPointers */);
-    EXPECT_TRUE(mPlaybackMQ);
-    struct PlaybackThreadArgs* threadArgs =
-            (struct PlaybackThreadArgs*)malloc(sizeof(struct PlaybackThreadArgs));
-    threadArgs->user = this;
-    threadArgs->playbackConf = &playbackConf;
-    threadArgs->keepWritingPlaybackFMQ = &mKeepWritingPlaybackFMQ;
-
-    pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, (void*)threadArgs);
-    pthread_setname_np(mPlaybackThread, "test_playback_input_loop");
-}
-
-void DvrCallback::stopPlaybackThread() {
-    mPlaybackThreadRunning = false;
-    mKeepWritingPlaybackFMQ = false;
-
-    android::Mutex::Autolock autoLock(mPlaybackThreadLock);
-}
-
-void* DvrCallback::__threadLoopPlayback(void* threadArgs) {
-    DvrCallback* const self =
-            static_cast<DvrCallback*>(((struct PlaybackThreadArgs*)threadArgs)->user);
-    self->playbackThreadLoop(((struct PlaybackThreadArgs*)threadArgs)->playbackConf,
-                             ((struct PlaybackThreadArgs*)threadArgs)->keepWritingPlaybackFMQ);
-    return 0;
-}
-
-void DvrCallback::playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ) {
-    android::Mutex::Autolock autoLock(mPlaybackThreadLock);
-    mPlaybackThreadRunning = true;
-
-    // Create the EventFlag that is used to signal the HAL impl that data have been
-    // written into the Playback FMQ
-    EventFlag* playbackMQEventFlag;
-    EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
-                android::OK);
-
-    // open the stream and get its length
-    std::ifstream inputData(playbackConf->inputDataFile, std::ifstream::binary);
-    int writeSize = playbackConf->setting.packetSize * 6;
-    char* buffer = new char[writeSize];
-    ALOGW("[vts] playback thread loop start %s", playbackConf->inputDataFile.c_str());
-    if (!inputData.is_open()) {
-        mPlaybackThreadRunning = false;
-        ALOGW("[vts] Error %s", strerror(errno));
-    }
-
-    while (mPlaybackThreadRunning) {
-        // move the stream pointer for packet size * 6 every read until the end
-        while (*keepWritingPlaybackFMQ) {
-            inputData.read(buffer, writeSize);
-            if (!inputData) {
-                int leftSize = inputData.gcount();
-                if (leftSize == 0) {
-                    mPlaybackThreadRunning = false;
-                    break;
-                }
-                inputData.clear();
-                inputData.read(buffer, leftSize);
-                // Write the left over of the input data and quit the thread
-                if (leftSize > 0) {
-                    EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], leftSize));
-                    playbackMQEventFlag->wake(
-                            static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
-                }
-                mPlaybackThreadRunning = false;
-                break;
-            }
-            // Write input FMQ and notify the Tuner Implementation
-            EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], writeSize));
-            playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
-            inputData.seekg(writeSize, inputData.cur);
-            sleep(1);
-        }
-    }
-
-    ALOGW("[vts] Playback thread end.");
-
-    delete[] buffer;
-    inputData.close();
-}
-
-void DvrCallback::testRecordOutput() {
-    android::Mutex::Autolock autoLock(mMsgLock);
-    while (mDataOutputBuffer.empty()) {
-        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
-            EXPECT_TRUE(false) << "record output matching pid does not output within timeout";
-            return;
-        }
-    }
-    stopRecordThread();
-    ALOGW("[vts] record pass and stop");
-}
-
-void DvrCallback::startRecordOutputThread(RecordSettings recordSetting,
-                                          MQDesc& recordMQDescriptor) {
-    mRecordMQ = std::make_unique<FilterMQ>(recordMQDescriptor, true /* resetPointers */);
-    EXPECT_TRUE(mRecordMQ);
-    struct RecordThreadArgs* threadArgs =
-            (struct RecordThreadArgs*)malloc(sizeof(struct RecordThreadArgs));
-    threadArgs->user = this;
-    threadArgs->recordSetting = &recordSetting;
-    threadArgs->keepReadingRecordFMQ = &mKeepReadingRecordFMQ;
-
-    pthread_create(&mRecordThread, NULL, __threadLoopRecord, (void*)threadArgs);
-    pthread_setname_np(mRecordThread, "test_record_input_loop");
-}
-
-void* DvrCallback::__threadLoopRecord(void* threadArgs) {
-    DvrCallback* const self =
-            static_cast<DvrCallback*>(((struct RecordThreadArgs*)threadArgs)->user);
-    self->recordThreadLoop(((struct RecordThreadArgs*)threadArgs)->recordSetting,
-                           ((struct RecordThreadArgs*)threadArgs)->keepReadingRecordFMQ);
-    return 0;
-}
-
-void DvrCallback::recordThreadLoop(RecordSettings* /*recordSetting*/, bool* keepReadingRecordFMQ) {
-    ALOGD("[vts] DvrCallback record threadLoop start.");
-    android::Mutex::Autolock autoLock(mRecordThreadLock);
-    mRecordThreadRunning = true;
-
-    // Create the EventFlag that is used to signal the HAL impl that data have been
-    // read from the Record FMQ
-    EventFlag* recordMQEventFlag;
-    EXPECT_TRUE(EventFlag::createEventFlag(mRecordMQ->getEventFlagWord(), &recordMQEventFlag) ==
-                android::OK);
-
-    while (mRecordThreadRunning) {
-        while (*keepReadingRecordFMQ) {
-            uint32_t efState = 0;
-            android::status_t status = recordMQEventFlag->wait(
-                    static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT,
-                    true /* retry on spurious wake */);
-            if (status != android::OK) {
-                ALOGD("[vts] wait for data ready on the record FMQ");
-                continue;
-            }
-            // Our current implementation filter the data and write it into the filter FMQ
-            // immediately after the DATA_READY from the VTS/framework
-            if (!readRecordFMQ()) {
-                ALOGD("[vts] record data failed to be filtered. Ending thread");
-                mRecordThreadRunning = false;
-                break;
-            }
-        }
-    }
-
-    mRecordThreadRunning = false;
-    ALOGD("[vts] record thread ended.");
-}
-
-bool DvrCallback::readRecordFMQ() {
-    android::Mutex::Autolock autoLock(mMsgLock);
-    bool result = false;
-    mDataOutputBuffer.clear();
-    mDataOutputBuffer.resize(mRecordMQ->availableToRead());
-    result = mRecordMQ->read(mDataOutputBuffer.data(), mRecordMQ->availableToRead());
-    EXPECT_TRUE(result) << "can't read from Record MQ";
-    mMsgCondition.signal();
-    return result;
-}
-
-void DvrCallback::stopRecordThread() {
-    mKeepReadingRecordFMQ = false;
-    mRecordThreadRunning = false;
-    android::Mutex::Autolock autoLock(mRecordThreadLock);
-}
-/********************************** End DvrCallback ************************************/
-
 /*======================== Start Descrambler APIs Tests Implementation ========================*/
-AssertionResult TunerHidlTest::createDescrambler() {
+AssertionResult TunerHidlTest::createDescrambler(uint32_t demuxId) {
     Result status;
     mService->openDescrambler([&](Result result, const sp<IDescrambler>& descrambler) {
         mDescrambler = descrambler;
@@ -201,21 +28,19 @@
         return failure();
     }
 
-    status = mDescrambler->setDemuxSource(mDemuxId);
+    status = mDescrambler->setDemuxSource(demuxId);
     if (status != Result::SUCCESS) {
         return failure();
     }
 
     // Test if demux source can be set more than once.
-    status = mDescrambler->setDemuxSource(mDemuxId);
+    status = mDescrambler->setDemuxSource(demuxId);
     return AssertionResult(status == Result::INVALID_STATE);
 }
 
 AssertionResult TunerHidlTest::closeDescrambler() {
     Result status;
-    if (!mDescrambler && createDescrambler() == failure()) {
-        return failure();
-    }
+    EXPECT_TRUE(mDescrambler);
 
     status = mDescrambler->close();
     mDescrambler = nullptr;
@@ -223,40 +48,6 @@
 }
 /*========================= End Descrambler APIs Tests Implementation =========================*/
 
-/*============================ Start Dvr APIs Tests Implementation ============================*/
-AssertionResult TunerHidlTest::openDvrInDemux(DvrType type) {
-    Result status;
-
-    // Create dvr callback
-    mDvrCallback = new DvrCallback();
-
-    mDemux->openDvr(type, FMQ_SIZE_1M, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
-        mDvr = dvr;
-        status = result;
-    });
-
-    return AssertionResult(status == Result::SUCCESS);
-}
-
-AssertionResult TunerHidlTest::configDvr(DvrSettings setting) {
-    Result status = mDvr->configure(setting);
-
-    return AssertionResult(status == Result::SUCCESS);
-}
-
-AssertionResult TunerHidlTest::getDvrMQDescriptor() {
-    Result status;
-    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
-
-    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
-        mDvrMQDescriptor = dvrMQDesc;
-        status = result;
-    });
-
-    return AssertionResult(status == Result::SUCCESS);
-}
-/*============================ End Dvr APIs Tests Implementation ============================*/
-
 /*========================== Start Data Flow Tests Implementation ==========================*/
 AssertionResult TunerHidlTest::broadcastDataFlowTest(vector<string> /*goldenOutputFiles*/) {
     // Data Verify Module
@@ -417,6 +208,10 @@
 void TunerHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
                                               FrontendConfig frontendConf) {
     uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    uint32_t filterId;
+
     mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
     if (feId == INVALID_ID) {
         // TODO broadcast test on Cuttlefish needs licensed ts input,
@@ -426,13 +221,12 @@
     }
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(mDemux, mDemuxId));
-    mFilterTests.setDemux(mDemux);
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    mFilterTests.setDemux(demux);
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
-    uint32_t filterId;
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterConf.setting, filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
     ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     // tune test
@@ -445,6 +239,67 @@
     ASSERT_TRUE(mDemuxTests.closeDemux());
     ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
+
+void TunerDvrHidlTest::attachSingleFilterToDvrTest(FilterConfig filterConf,
+                                                   FrontendConfig frontendConf, DvrConfig dvrConf) {
+    description("Open and configure a Dvr in Demux.");
+    uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    uint32_t filterId;
+    sp<IFilter> filter;
+
+    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));
+    mFilterTests.setDemux(demux);
+    mDvrTests.setDemux(demux);
+    ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type));
+    ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
+    ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
+    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    filter = mFilterTests.getFilterById(filterId);
+    ASSERT_TRUE(filter != nullptr);
+    ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
+    ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+    mDvrTests.closeDvr();
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
+void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf,
+                                                        FrontendConfig frontendConf) {
+    uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    uint32_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));
+    mFilterTests.setDemux(demux);
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
+    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
 /*================================== End Test Module ==================================*/
 /***************************** End Test Implementation *****************************/
 
@@ -467,50 +322,56 @@
 TEST_P(TunerDemuxHidlTest, openDemux) {
     description("Open and close a Demux.");
     uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
     mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(mDemux, mDemuxId));
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     ASSERT_TRUE(mDemuxTests.closeDemux());
 }
 
 TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
     description("Open and start a filter in Demux.");
-    uint32_t feId;
-    mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(mDemux, mDemuxId));
-    mFilterTests.setDemux(mDemux);
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
-    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterArray[TS_VIDEO0].type));
-    uint32_t filterId;
-    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterArray[TS_VIDEO0].setting, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
-    ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
-    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
-    ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+    // TODO use paramterized tests
+    configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+}
+
+TEST_P(TunerDvrHidlTest, AttachFiltersToRecordTest) {
+    description("Attach a single filter to the record dvr test.");
+    // TODO use paramterized tests
+    attachSingleFilterToDvrTest(filterArray[TS_VIDEO0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
+}
+
+TEST_P(TunerDvrHidlTest, AttachFiltersToPlaybackTest) {
+    description("Attach a single filter to the playback dvr test.");
+    // TODO use paramterized tests
+    attachSingleFilterToDvrTest(filterArray[TS_VIDEO0], frontendArray[DVBT],
+                                dvrArray[DVR_PLAYBACK0]);
 }
 
 /*============================ Start Descrambler Tests ============================*/
 /*
  * TODO: re-enable the tests after finalizing the test refactoring.
  */
-/*TEST_P(TunerHidlTest, CreateDescrambler) {
+TEST_P(TunerHidlTest, CreateDescrambler) {
     description("Create Descrambler");
-    ASSERT_TRUE(createDescrambler());
+    uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    mFrontendTests.getFrontendIdByType(frontendArray[DVBT].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));
+    ASSERT_TRUE(createDescrambler(demuxId));
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(closeDescrambler());
 }
 
-TEST_P(TunerHidlTest, CloseDescrambler) {
-    description("Close Descrambler");
-    ASSERT_TRUE(closeDescrambler());
-}*/
 /*============================== End Descrambler Tests ==============================*/
 
 /*============================== Start Data Flow Tests ==============================*/
@@ -642,4 +503,9 @@
         PerInstance, TunerFilterHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
         android::hardware::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, TunerDvrHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
+        android::hardware::PrintInstanceNameToString);
 }  // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
index f177047..37b2866 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
@@ -15,30 +15,13 @@
  */
 
 #include <android/hardware/tv/tuner/1.0/IDescrambler.h>
-#include <android/hardware/tv/tuner/1.0/IDvr.h>
-#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
-#include <fstream>
-#include <iostream>
 
 #include "DemuxTests.h"
-#include "FilterTests.h"
+#include "DvrTests.h"
 #include "FrontendTests.h"
 
 using android::hardware::tv::tuner::V1_0::DataFormat;
-using android::hardware::tv::tuner::V1_0::DvrSettings;
-using android::hardware::tv::tuner::V1_0::DvrType;
 using android::hardware::tv::tuner::V1_0::IDescrambler;
-using android::hardware::tv::tuner::V1_0::IDvr;
-using android::hardware::tv::tuner::V1_0::IDvrCallback;
-using android::hardware::tv::tuner::V1_0::PlaybackSettings;
-using android::hardware::tv::tuner::V1_0::PlaybackStatus;
-using android::hardware::tv::tuner::V1_0::RecordSettings;
-using android::hardware::tv::tuner::V1_0::RecordStatus;
-
-struct PlaybackConf {
-    string inputDataFile;
-    PlaybackSettings setting;
-};
 
 static AssertionResult failure() {
     return ::testing::AssertionFailure();
@@ -50,89 +33,6 @@
 
 namespace {
 
-class DvrCallback : public IDvrCallback {
-  public:
-    virtual Return<void> onRecordStatus(DemuxFilterStatus status) override {
-        ALOGW("[vts] record status %hhu", status);
-        switch (status) {
-            case DemuxFilterStatus::DATA_READY:
-                break;
-            case DemuxFilterStatus::LOW_WATER:
-                break;
-            case DemuxFilterStatus::HIGH_WATER:
-            case DemuxFilterStatus::OVERFLOW:
-                ALOGW("[vts] record overflow. Flushing");
-                break;
-        }
-        return Void();
-    }
-
-    virtual Return<void> onPlaybackStatus(PlaybackStatus status) override {
-        // android::Mutex::Autolock autoLock(mMsgLock);
-        ALOGW("[vts] playback status %d", status);
-        switch (status) {
-            case PlaybackStatus::SPACE_EMPTY:
-            case PlaybackStatus::SPACE_ALMOST_EMPTY:
-                ALOGW("[vts] keep playback inputing %d", status);
-                mKeepWritingPlaybackFMQ = true;
-                break;
-            case PlaybackStatus::SPACE_ALMOST_FULL:
-            case PlaybackStatus::SPACE_FULL:
-                ALOGW("[vts] stop playback inputing %d", status);
-                mKeepWritingPlaybackFMQ = false;
-                break;
-        }
-        return Void();
-    }
-
-    void testFilterDataOutput();
-    void stopPlaybackThread();
-    void testRecordOutput();
-    void stopRecordThread();
-
-    void startPlaybackInputThread(PlaybackConf playbackConf, MQDesc& playbackMQDescriptor);
-    void startRecordOutputThread(RecordSettings recordSetting, MQDesc& recordMQDescriptor);
-    static void* __threadLoopPlayback(void* threadArgs);
-    static void* __threadLoopRecord(void* threadArgs);
-    void playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ);
-    void recordThreadLoop(RecordSettings* recordSetting, bool* keepWritingPlaybackFMQ);
-
-    bool readRecordFMQ();
-
-  private:
-    struct PlaybackThreadArgs {
-        DvrCallback* user;
-        PlaybackConf* playbackConf;
-        bool* keepWritingPlaybackFMQ;
-    };
-    struct RecordThreadArgs {
-        DvrCallback* user;
-        RecordSettings* recordSetting;
-        bool* keepReadingRecordFMQ;
-    };
-    uint16_t mDataLength = 0;
-    std::vector<uint8_t> mDataOutputBuffer;
-
-    std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterMQ;
-    std::unique_ptr<FilterMQ> mPlaybackMQ;
-    std::unique_ptr<FilterMQ> mRecordMQ;
-    std::map<uint32_t, EventFlag*> mFilterMQEventFlag;
-
-    android::Mutex mMsgLock;
-    android::Mutex mPlaybackThreadLock;
-    android::Mutex mRecordThreadLock;
-    android::Condition mMsgCondition;
-
-    bool mKeepWritingPlaybackFMQ = true;
-    bool mKeepReadingRecordFMQ = true;
-    bool mPlaybackThreadRunning;
-    bool mRecordThreadRunning;
-    pthread_t mPlaybackThread;
-    pthread_t mRecordThread;
-
-    int mPidFilterOutputCount = 0;
-};
-
 class TunerFrontendHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
@@ -174,8 +74,6 @@
     sp<ITuner> mService;
     FrontendTests mFrontendTests;
     DemuxTests mDemuxTests;
-    sp<IDemux> mDemux;
-    uint32_t mDemuxId;
 };
 
 class TunerFilterHidlTest : public testing::TestWithParam<std::string> {
@@ -197,21 +95,47 @@
         RecordProperty("description", description);
     }
 
+    void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
+
     sp<ITuner> mService;
     FrontendTests mFrontendTests;
     DemuxTests mDemuxTests;
     FilterTests mFilterTests;
-    sp<IDemux> mDemux;
-    uint32_t mDemuxId;
+};
+
+class TunerDvrHidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        mService = ITuner::getService(GetParam());
+        ASSERT_NE(mService, nullptr);
+        initFrontendConfig();
+        initFrontendScanConfig();
+        initFilterConfig();
+        initDvrConfig();
+
+        mFrontendTests.setService(mService);
+        mDemuxTests.setService(mService);
+        mFilterTests.setService(mService);
+        mDvrTests.setService(mService);
+    }
+
+  protected:
+    static void description(const std::string& description) {
+        RecordProperty("description", description);
+    }
+
+    void attachSingleFilterToDvrTest(FilterConfig filterConf, FrontendConfig frontendConf,
+                                     DvrConfig dvrConf);
+
+    sp<ITuner> mService;
+    FrontendTests mFrontendTests;
+    DemuxTests mDemuxTests;
+    FilterTests mFilterTests;
+    DvrTests mDvrTests;
 };
 
 class TunerHidlTest : public testing::TestWithParam<std::string> {
   public:
-    sp<ITuner> mService;
-    FrontendTests mFrontendTests;
-    DemuxTests mDemuxTests;
-    FilterTests mFilterTests;
-
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
@@ -229,23 +153,14 @@
         RecordProperty("description", description);
     }
 
+    sp<ITuner> mService;
+    FrontendTests mFrontendTests;
+    DemuxTests mDemuxTests;
+    FilterTests mFilterTests;
+
     sp<IDescrambler> mDescrambler;
-    sp<IDvr> mDvr;
-    sp<IDemux> mDemux;
-    uint32_t mDemuxId;
 
-    sp<DvrCallback> mDvrCallback;
-    MQDesc mDvrMQDescriptor;
-    MQDesc mRecordMQDescriptor;
-
-    pthread_t mPlaybackshread;
-    bool mPlaybackThreadRunning;
-
-    AssertionResult openDvrInDemux(DvrType type);
-    AssertionResult configDvr(DvrSettings setting);
-    AssertionResult getDvrMQDescriptor();
-
-    AssertionResult createDescrambler();
+    AssertionResult createDescrambler(uint32_t demuxId);
     AssertionResult closeDescrambler();
 
     AssertionResult playbackDataFlowTest(vector<FilterConfig> filterConf, PlaybackConf playbackConf,
@@ -257,4 +172,4 @@
 
     void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
 };
-}  // namespace
\ No newline at end of file
+}  // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index 538773a..a74fa02 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -21,12 +21,15 @@
 #include <hidl/Status.h>
 #include <hidlmemory/FrameworkUtils.h>
 
+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::DemuxFilterSettings;
 using android::hardware::tv::tuner::V1_0::DemuxFilterType;
 using android::hardware::tv::tuner::V1_0::DemuxTpid;
 using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using android::hardware::tv::tuner::V1_0::DvrSettings;
+using android::hardware::tv::tuner::V1_0::DvrType;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
@@ -37,6 +40,8 @@
 using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
 using android::hardware::tv::tuner::V1_0::FrontendSettings;
 using android::hardware::tv::tuner::V1_0::FrontendType;
+using android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using android::hardware::tv::tuner::V1_0::RecordSettings;
 
 using namespace std;
 
@@ -62,9 +67,15 @@
     SCAN_MAX,
 } FrontendScan;
 
+typedef enum {
+    DVR_RECORD0,
+    DVR_PLAYBACK0,
+    DVR_MAX,
+} Dvr;
+
 struct FilterConfig {
     DemuxFilterType type;
-    DemuxFilterSettings setting;
+    DemuxFilterSettings settings;
 };
 
 struct FrontendConfig {
@@ -80,10 +91,16 @@
     DemuxTpid audioPid;
 };
 
+struct DvrConfig {
+    DvrType type;
+    DvrSettings settings;
+};
+
 static FrontendConfig frontendArray[FILTER_MAX];
 static FrontendConfig frontendScanArray[SCAN_MAX];
 static ChannelConfig channelArray[FRONTEND_MAX];
 static FilterConfig filterArray[FILTER_MAX];
+static DvrConfig dvrArray[DVR_MAX];
 static vector<string> goldenOutputFiles;
 
 /** Configuration array for the frontend tune test */
@@ -126,40 +143,62 @@
     // TS VIDEO filter setting for default implementation testing
     filterArray[TS_VIDEO0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_VIDEO0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
-    filterArray[TS_VIDEO0].setting.ts().tpid = 119;
-    filterArray[TS_VIDEO0].setting.ts().filterSettings.av({.isPassthrough = false});
+    filterArray[TS_VIDEO0].settings.ts().tpid = 119;
+    filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false});
     filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
-    filterArray[TS_VIDEO1].setting.ts().tpid = 81;
-    filterArray[TS_VIDEO1].setting.ts().filterSettings.av({.isPassthrough = false});
+    filterArray[TS_VIDEO1].settings.ts().tpid = 81;
+    filterArray[TS_VIDEO1].settings.ts().filterSettings.av({.isPassthrough = false});
     // TS AUDIO filter setting
     filterArray[TS_AUDIO0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_AUDIO0].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
-    filterArray[TS_AUDIO0].setting.ts().tpid = 84;
-    filterArray[TS_AUDIO0].setting.ts().filterSettings.av({.isPassthrough = false});
+    filterArray[TS_AUDIO0].settings.ts().tpid = 84;
+    filterArray[TS_AUDIO0].settings.ts().filterSettings.av({.isPassthrough = false});
     // TS PES filter setting
     filterArray[TS_PES0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_PES0].type.subType.tsFilterType(DemuxTsFilterType::PES);
-    filterArray[TS_PES0].setting.ts().tpid = 256;
-    filterArray[TS_PES0].setting.ts().filterSettings.pesData({
+    filterArray[TS_PES0].settings.ts().tpid = 256;
+    filterArray[TS_PES0].settings.ts().filterSettings.pesData({
             .isRaw = false,
             .streamId = 0xbd,
     });
     // TS PCR filter setting
     filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR);
-    filterArray[TS_PCR0].setting.ts().tpid = 81;
-    filterArray[TS_PCR0].setting.ts().filterSettings.noinit();
+    filterArray[TS_PCR0].settings.ts().tpid = 81;
+    filterArray[TS_PCR0].settings.ts().filterSettings.noinit();
     // TS filter setting
     filterArray[TS_TS0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_TS0].type.subType.tsFilterType(DemuxTsFilterType::TS);
-    filterArray[TS_TS0].setting.ts().tpid = 48;
-    filterArray[TS_TS0].setting.ts().filterSettings.noinit();
+    filterArray[TS_TS0].settings.ts().tpid = 48;
+    filterArray[TS_TS0].settings.ts().filterSettings.noinit();
     // TS SECTION filter setting
     filterArray[TS_SECTION0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_SECTION0].type.subType.tsFilterType(DemuxTsFilterType::SECTION);
-    filterArray[TS_SECTION0].setting.ts().tpid = 48;
-    filterArray[TS_SECTION0].setting.ts().filterSettings.section({
+    filterArray[TS_SECTION0].settings.ts().tpid = 48;
+    filterArray[TS_SECTION0].settings.ts().filterSettings.section({
             .isRaw = false,
     });
 };
+
+/** Configuration array for the dvr test */
+inline void initDvrConfig() {
+    RecordSettings recordSettings{
+            .statusMask = 0xf,
+            .lowThreshold = 0x1000,
+            .highThreshold = 0x07fff,
+            .dataFormat = DataFormat::TS,
+            .packetSize = 188,
+    };
+    dvrArray[DVR_RECORD0].type = DvrType::RECORD;
+    dvrArray[DVR_RECORD0].settings.record(recordSettings);
+    PlaybackSettings playbackSettings{
+            .statusMask = 0xf,
+            .lowThreshold = 0x1000,
+            .highThreshold = 0x07fff,
+            .dataFormat = DataFormat::TS,
+            .packetSize = 188,
+    };
+    dvrArray[DVR_PLAYBACK0].type = DvrType::PLAYBACK;
+    dvrArray[DVR_PLAYBACK0].settings.playback(playbackSettings);
+};
\ No newline at end of file