Merge changes from topic "record" am: e843b7c118 am: 360776a89e

Change-Id: Idfc4ab4cc53b2f63fb353c3d68952262d3719b72
diff --git a/tv/tuner/1.0/IFilter.hal b/tv/tuner/1.0/IFilter.hal
index 3ed09f6..94e3c0c 100644
--- a/tv/tuner/1.0/IFilter.hal
+++ b/tv/tuner/1.0/IFilter.hal
@@ -104,11 +104,11 @@
      *
      * It is used by the client to ask the hardware resource id for the filter.
      *
-     * @param filterId the hardware resource Id for the filter.
      * @return result Result status of the operation.
      *         SUCCESS if successful,
      *         INVALID_STATE if failed for wrong state.
      *         UNKNOWN_ERROR if failed for other reasons.
+     * @return filterId the hardware resource Id for the filter.
      */
     getId() generates (Result result, uint32_t filterId);
 
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index 71a26ab..43c4e3a 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -51,7 +51,8 @@
     mFrontendSourceFile = mFrontend->getSourceFile();
 
     mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
-    return startBroadcastInputLoop();
+
+    return startFrontendInputLoop();
 }
 
 Return<void> Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize,
@@ -136,14 +137,14 @@
         return Void();
     }
 
-    sp<Dvr> dvr = new Dvr(type, bufferSize, cb, this);
+    mDvr = new Dvr(type, bufferSize, cb, this);
 
-    if (!dvr->createDvrMQ()) {
-        _hidl_cb(Result::UNKNOWN_ERROR, dvr);
+    if (!mDvr->createDvrMQ()) {
+        _hidl_cb(Result::UNKNOWN_ERROR, mDvr);
         return Void();
     }
 
-    _hidl_cb(Result::SUCCESS, dvr);
+    _hidl_cb(Result::SUCCESS, mDvr);
     return Void();
 }
 
@@ -166,13 +167,14 @@
 
     // resetFilterRecords(filterId);
     mUsedFilterIds.erase(filterId);
+    mRecordFilterIds.erase(filterId);
     mUnusedFilterIds.insert(filterId);
     mFilters.erase(filterId);
 
     return Result::SUCCESS;
 }
 
-void Demux::startTsFilter(vector<uint8_t> data) {
+void Demux::startBroadcastTsFilter(vector<uint8_t> data) {
     set<uint32_t>::iterator it;
     for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
         uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
@@ -185,7 +187,17 @@
     }
 }
 
-bool Demux::startFilterDispatcher() {
+void Demux::sendFrontendInputToRecord(vector<uint8_t> data) {
+    set<uint32_t>::iterator it;
+    for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) {
+        if (DEBUG_FILTER) {
+            ALOGW("update record filter output");
+        }
+        mFilters[*it]->updateRecordOutput(data);
+    }
+}
+
+bool Demux::startBroadcastFilterDispatcher() {
     set<uint32_t>::iterator it;
 
     // Handle the output data per filter type
@@ -198,6 +210,18 @@
     return true;
 }
 
+bool Demux::startRecordFilterDispatcher() {
+    set<uint32_t>::iterator it;
+
+    for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) {
+        if (mFilters[*it]->startRecordFilterHandler() != Result::SUCCESS) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 Result Demux::startFilterHandler(uint32_t filterId) {
     return mFilters[filterId]->startFilterHandler();
 }
@@ -210,22 +234,22 @@
     return mFilters[filterId]->getTpid();
 }
 
-Result Demux::startBroadcastInputLoop() {
-    pthread_create(&mBroadcastInputThread, NULL, __threadLoopBroadcast, this);
-    pthread_setname_np(mBroadcastInputThread, "broadcast_input_thread");
+Result Demux::startFrontendInputLoop() {
+    pthread_create(&mFrontendInputThread, NULL, __threadLoopFrontend, this);
+    pthread_setname_np(mFrontendInputThread, "frontend_input_thread");
 
     return Result::SUCCESS;
 }
 
-void* Demux::__threadLoopBroadcast(void* user) {
+void* Demux::__threadLoopFrontend(void* user) {
     Demux* const self = static_cast<Demux*>(user);
-    self->broadcastInputThreadLoop();
+    self->frontendInputThreadLoop();
     return 0;
 }
 
-void Demux::broadcastInputThreadLoop() {
-    std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
-    mBroadcastInputThreadRunning = true;
+void Demux::frontendInputThreadLoop() {
+    std::lock_guard<std::mutex> lock(mFrontendInputThreadLock);
+    mFrontendInputThreadRunning = true;
     mKeepFetchingDataFromFrontend = true;
 
     // open the stream and get its length
@@ -234,20 +258,20 @@
     int packetSize = 188;
     int writePacketAmount = 6;
     char* buffer = new char[packetSize];
-    ALOGW("[Demux] broadcast input thread loop start %s", mFrontendSourceFile.c_str());
+    ALOGW("[Demux] Frontend input thread loop start %s", mFrontendSourceFile.c_str());
     if (!inputData.is_open()) {
-        mBroadcastInputThreadRunning = false;
+        mFrontendInputThreadRunning = false;
         ALOGW("[Demux] Error %s", strerror(errno));
     }
 
-    while (mBroadcastInputThreadRunning) {
+    while (mFrontendInputThreadRunning) {
         // move the stream pointer for packet size * 6 every read until the end
         while (mKeepFetchingDataFromFrontend) {
             for (int i = 0; i < writePacketAmount; i++) {
                 inputData.read(buffer, packetSize);
                 if (!inputData) {
                     mKeepFetchingDataFromFrontend = false;
-                    mBroadcastInputThreadRunning = false;
+                    mFrontendInputThreadRunning = false;
                     break;
                 }
                 // filter and dispatch filter output
@@ -256,23 +280,61 @@
                 for (int index = 0; index < byteBuffer.size(); index++) {
                     byteBuffer[index] = static_cast<uint8_t>(buffer[index]);
                 }
-                startTsFilter(byteBuffer);
+                if (mIsRecording) {
+                    // Feed the data into the Dvr recording input
+                    sendFrontendInputToRecord(byteBuffer);
+                } else {
+                    // Feed the data into the broadcast demux filter
+                    startBroadcastTsFilter(byteBuffer);
+                }
             }
-            startFilterDispatcher();
+            if (mIsRecording) {
+                // Dispatch the data into the broadcasting filters.
+                startRecordFilterDispatcher();
+            } else {
+                // Dispatch the data into the broadcasting filters.
+                startBroadcastFilterDispatcher();
+            }
             usleep(100);
         }
     }
 
-    ALOGW("[Demux] Broadcast Input thread end.");
+    ALOGW("[Demux] Frontend Input thread end.");
     delete[] buffer;
     inputData.close();
 }
 
-void Demux::stopBroadcastInput() {
+void Demux::stopFrontendInput() {
     ALOGD("[Demux] stop frontend on demux");
     mKeepFetchingDataFromFrontend = false;
-    mBroadcastInputThreadRunning = false;
-    std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
+    mFrontendInputThreadRunning = false;
+    std::lock_guard<std::mutex> lock(mFrontendInputThreadLock);
+}
+
+void Demux::setIsRecording(bool isRecording) {
+    mIsRecording = isRecording;
+}
+
+bool Demux::attachRecordFilter(int filterId) {
+    if (mFilters[filterId] == nullptr || mDvr == nullptr) {
+        return false;
+    }
+
+    mRecordFilterIds.insert(filterId);
+    mFilters[filterId]->attachFilterToRecord(mDvr);
+
+    return true;
+}
+
+bool Demux::detachRecordFilter(int filterId) {
+    if (mFilters[filterId] == nullptr || mDvr == nullptr) {
+        return false;
+    }
+
+    mRecordFilterIds.erase(filterId);
+    mFilters[filterId]->detachFilterFromRecord();
+
+    return true;
 }
 
 }  // namespace implementation
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 037429d..1405d0c 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -81,11 +81,14 @@
     virtual Return<Result> disconnectCiCam() override;
 
     // Functions interacts with Tuner Service
-    void stopBroadcastInput();
+    void stopFrontendInput();
     Result removeFilter(uint32_t filterId);
+    bool attachRecordFilter(int filterId);
+    bool detachRecordFilter(int filterId);
     Result startFilterHandler(uint32_t filterId);
     void updateFilterOutput(uint16_t filterId, vector<uint8_t> data);
     uint16_t getFilterTpid(uint32_t filterId);
+    void setIsRecording(bool isRecording);
 
   private:
     // Tuner service
@@ -101,9 +104,9 @@
         uint32_t filterId;
     };
 
-    Result startBroadcastInputLoop();
-    static void* __threadLoopBroadcast(void* user);
-    void broadcastInputThreadLoop();
+    Result startFrontendInputLoop();
+    static void* __threadLoopFrontend(void* user);
+    void frontendInputThreadLoop();
 
     /**
      * To create a FilterMQ with the the next available Filter ID.
@@ -117,9 +120,13 @@
     /**
      * A dispatcher to read and dispatch input data to all the started filters.
      * Each filter handler handles the data filtering/output writing/filterEvent updating.
+     * Note that recording filters are not included.
      */
-    bool startFilterDispatcher();
-    void startTsFilter(vector<uint8_t> data);
+    bool startBroadcastFilterDispatcher();
+    void startBroadcastTsFilter(vector<uint8_t> data);
+
+    void sendFrontendInputToRecord(vector<uint8_t> data);
+    bool startRecordFilterDispatcher();
 
     uint32_t mDemuxId;
     uint32_t mCiCamId;
@@ -141,26 +148,40 @@
      */
     set<uint32_t> mUnusedFilterIds;
     /**
+     * Record all the attached record filter Ids.
+     * Any removed filter id should be removed from this set.
+     */
+    set<uint32_t> mRecordFilterIds;
+    /**
      * A list of created FilterMQ ptrs.
      * The array number is the filter ID.
      */
     std::map<uint32_t, sp<Filter>> mFilters;
 
+    /**
+     * Local reference to the opened DVR object.
+     */
+    sp<Dvr> mDvr;
+
     // Thread handlers
-    pthread_t mBroadcastInputThread;
+    pthread_t mFrontendInputThread;
     /**
      * If a specific filter's writing loop is still running
      */
-    bool mBroadcastInputThreadRunning;
+    bool mFrontendInputThreadRunning;
     bool mKeepFetchingDataFromFrontend;
     /**
+     * If the dvr recording is running.
+     */
+    bool mIsRecording = false;
+    /**
      * Lock to protect writes to the FMQs
      */
     std::mutex mWriteLock;
     /**
      * Lock to protect writes to the input status
      */
-    std::mutex mBroadcastInputThreadLock;
+    std::mutex mFrontendInputThreadLock;
 
     // temp handle single PES filter
     // TODO handle mulptiple Pes filters
diff --git a/tv/tuner/1.0/default/Dvr.cpp b/tv/tuner/1.0/default/Dvr.cpp
index eb38f90..3088a9d 100644
--- a/tv/tuner/1.0/default/Dvr.cpp
+++ b/tv/tuner/1.0/default/Dvr.cpp
@@ -70,7 +70,14 @@
         return status;
     }
 
+    // check if the attached filter is a record filter
+
     mFilters[filterId] = filter;
+    mIsRecordFilterAttached = true;
+    if (!mDemux->attachRecordFilter(filterId)) {
+        return Result::INVALID_ARGUMENT;
+    }
+    mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
 
     return Result::SUCCESS;
 }
@@ -95,6 +102,15 @@
     it = mFilters.find(filterId);
     if (it != mFilters.end()) {
         mFilters.erase(filterId);
+        if (!mDemux->detachRecordFilter(filterId)) {
+            return Result::INVALID_ARGUMENT;
+        }
+    }
+
+    // If all the filters are detached, record can't be started
+    if (mFilters.empty()) {
+        mIsRecordFilterAttached = false;
+        mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
     }
 
     return Result::SUCCESS;
@@ -115,8 +131,9 @@
         pthread_create(&mDvrThread, NULL, __threadLoopPlayback, this);
         pthread_setname_np(mDvrThread, "playback_waiting_loop");
     } else if (mType == DvrType::RECORD) {
-        /*pthread_create(&mInputThread, NULL, __threadLoopInput, this);
-        pthread_setname_np(mInputThread, "playback_waiting_loop");*/
+        mRecordStatus = RecordStatus::DATA_READY;
+        mIsRecordStarted = true;
+        mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
     }
 
     // TODO start another thread to send filter status callback to the framework
@@ -131,12 +148,17 @@
 
     std::lock_guard<std::mutex> lock(mDvrThreadLock);
 
+    mIsRecordStarted = false;
+    mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
+
     return Result::SUCCESS;
 }
 
 Return<Result> Dvr::flush() {
     ALOGV("%s", __FUNCTION__);
 
+    mRecordStatus = RecordStatus::DATA_READY;
+
     return Result::SUCCESS;
 }
 
@@ -272,6 +294,45 @@
     return true;
 }
 
+bool Dvr::writeRecordFMQ(const std::vector<uint8_t>& data) {
+    std::lock_guard<std::mutex> lock(mWriteLock);
+    ALOGW("[Dvr] write record FMQ");
+    if (mDvrMQ->write(data.data(), data.size())) {
+        mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+        maySendRecordStatusCallback();
+        return true;
+    }
+
+    maySendRecordStatusCallback();
+    return false;
+}
+
+void Dvr::maySendRecordStatusCallback() {
+    std::lock_guard<std::mutex> lock(mRecordStatusLock);
+    int availableToRead = mDvrMQ->availableToRead();
+    int availableToWrite = mDvrMQ->availableToWrite();
+
+    RecordStatus newStatus = checkRecordStatusChange(availableToWrite, availableToRead,
+                                                     mDvrSettings.record().highThreshold,
+                                                     mDvrSettings.record().lowThreshold);
+    if (mRecordStatus != newStatus) {
+        mCallback->onRecordStatus(newStatus);
+        mRecordStatus = newStatus;
+    }
+}
+
+RecordStatus Dvr::checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+                                          uint32_t highThreshold, uint32_t lowThreshold) {
+    if (availableToWrite == 0) {
+        return DemuxFilterStatus::OVERFLOW;
+    } else if (availableToRead > highThreshold) {
+        return DemuxFilterStatus::HIGH_WATER;
+    } else if (availableToRead < lowThreshold) {
+        return DemuxFilterStatus::LOW_WATER;
+    }
+    return mRecordStatus;
+}
+
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace tuner
diff --git a/tv/tuner/1.0/default/Dvr.h b/tv/tuner/1.0/default/Dvr.h
index fbb778c..f39d8db 100644
--- a/tv/tuner/1.0/default/Dvr.h
+++ b/tv/tuner/1.0/default/Dvr.h
@@ -79,6 +79,8 @@
      * Return false is any of the above processes fails.
      */
     bool createDvrMQ();
+    void sendBroadcastInputToDvrRecord(vector<uint8_t> byteBuffer);
+    bool writeRecordFMQ(const std::vector<uint8_t>& data);
 
   private:
     // Demux service
@@ -95,6 +97,8 @@
     void maySendRecordStatusCallback();
     PlaybackStatus checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
                                              uint32_t highThreshold, uint32_t lowThreshold);
+    RecordStatus checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+                                         uint32_t highThreshold, uint32_t lowThreshold);
     /**
      * A dispatcher to read and dispatch input data to all the started filters.
      * Each filter handler handles the data filtering/output writing/filterEvent updating.
@@ -103,9 +107,9 @@
     void startTpidFilter(vector<uint8_t> data);
     bool startFilterDispatcher();
     static void* __threadLoopPlayback(void* user);
-    static void* __threadLoopBroadcast(void* user);
+    static void* __threadLoopRecord(void* user);
     void playbackThreadLoop();
-    void broadcastInputThreadLoop();
+    void recordThreadLoop();
 
     unique_ptr<DvrMQ> mDvrMQ;
     EventFlag* mDvrEventFlag;
@@ -121,6 +125,7 @@
 
     // FMQ status local records
     PlaybackStatus mPlaybackStatus;
+    RecordStatus mRecordStatus;
     /**
      * If a specific filter's writing loop is still running
      */
@@ -135,10 +140,16 @@
      * Lock to protect writes to the input status
      */
     std::mutex mPlaybackStatusLock;
+    std::mutex mRecordStatusLock;
     std::mutex mBroadcastInputThreadLock;
     std::mutex mDvrThreadLock;
 
     const bool DEBUG_DVR = false;
+
+    // Booleans to check if recording is running.
+    // Recording is ready when both of the following are set to true.
+    bool mIsRecordStarted = false;
+    bool mIsRecordFilterAttached = false;
 };
 
 }  // namespace implementation
diff --git a/tv/tuner/1.0/default/Filter.cpp b/tv/tuner/1.0/default/Filter.cpp
index befd1e6..b3160fc 100644
--- a/tv/tuner/1.0/default/Filter.cpp
+++ b/tv/tuner/1.0/default/Filter.cpp
@@ -265,10 +265,16 @@
 
 void Filter::updateFilterOutput(vector<uint8_t> data) {
     std::lock_guard<std::mutex> lock(mFilterOutputLock);
-    ALOGD("[Filter] handler output updated");
+    ALOGD("[Filter] filter output updated");
     mFilterOutput.insert(mFilterOutput.end(), data.begin(), data.end());
 }
 
+void Filter::updateRecordOutput(vector<uint8_t> data) {
+    std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
+    ALOGD("[Filter] record filter output updated");
+    mRecordFilterOutput.insert(mRecordFilterOutput.end(), data.begin(), data.end());
+}
+
 Result Filter::startFilterHandler() {
     std::lock_guard<std::mutex> lock(mFilterOutputLock);
     switch (mType.mainType) {
@@ -292,12 +298,11 @@
                 case DemuxTsFilterType::PCR:
                     startPcrFilterHandler();
                     break;
-                case DemuxTsFilterType::RECORD:
-                    startRecordFilterHandler();
-                    break;
                 case DemuxTsFilterType::TEMI:
                     startTemiFilterHandler();
                     break;
+                default:
+                    break;
             }
             break;
         case DemuxFilterMainType::MMTP:
@@ -342,12 +347,16 @@
         if (mPesSizeLeft == 0) {
             uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
                               mFilterOutput[i + 6];
-            ALOGD("[Filter] prefix %d", prefix);
+            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;
-                ALOGD("[Filter] pes data length %d", mPesSizeLeft);
+                if (DEBUG_FILTER) {
+                    ALOGD("[Filter] pes data length %d", mPesSizeLeft);
+                }
             } else {
                 continue;
             }
@@ -360,7 +369,9 @@
         mPesOutput.insert(mPesOutput.end(), first, last);
         // size does not match then continue
         mPesSizeLeft -= endPoint;
-        ALOGD("[Filter] pes data left %d", mPesSizeLeft);
+        if (DEBUG_FILTER) {
+            ALOGD("[Filter] pes data left %d", mPesSizeLeft);
+        }
         if (mPesSizeLeft > 0) {
             continue;
         }
@@ -377,7 +388,9 @@
                 .streamId = mPesOutput[3],
                 .dataLength = static_cast<uint16_t>(mPesOutput.size()),
         };
-        ALOGD("[Filter] assembled pes data length %d", pesEvent.dataLength);
+        if (DEBUG_FILTER) {
+            ALOGD("[Filter] assembled pes data length %d", pesEvent.dataLength);
+        }
 
         int size = mFilterEvent.events.size();
         mFilterEvent.events.resize(size + 1);
@@ -413,13 +426,23 @@
 }
 
 Result Filter::startRecordFilterHandler() {
-    DemuxFilterTsRecordEvent tsRecordEvent;
+    /*DemuxFilterTsRecordEvent tsRecordEvent;
     tsRecordEvent.pid.tPid(0);
     tsRecordEvent.indexMask.tsIndexMask(0x01);
     mFilterEvent.events.resize(1);
     mFilterEvent.events[0].tsRecord(tsRecordEvent);
+*/
+    std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
+    if (mRecordFilterOutput.empty()) {
+        return Result::SUCCESS;
+    }
 
-    mFilterOutput.clear();
+    if (mDvr == nullptr || !mDvr->writeRecordFMQ(mRecordFilterOutput)) {
+        ALOGD("[Filter] dvr fails to write into record FMQ.");
+        return Result::UNKNOWN_ERROR;
+    }
+
+    mRecordFilterOutput.clear();
     return Result::SUCCESS;
 }
 
@@ -462,6 +485,14 @@
     return false;
 }
 
+void Filter::attachFilterToRecord(const sp<Dvr> dvr) {
+    mDvr = dvr;
+}
+
+void Filter::detachFilterFromRecord() {
+    mDvr = nullptr;
+}
+
 }  // 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 fbd965a..d397f73 100644
--- a/tv/tuner/1.0/default/Filter.h
+++ b/tv/tuner/1.0/default/Filter.h
@@ -22,6 +22,7 @@
 #include <math.h>
 #include <set>
 #include "Demux.h"
+#include "Dvr.h"
 #include "Frontend.h"
 
 using namespace std;
@@ -44,6 +45,7 @@
 using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
 
 class Demux;
+class Dvr;
 
 class Filter : public IFilter {
   public:
@@ -80,11 +82,17 @@
     bool createFilterMQ();
     uint16_t getTpid();
     void updateFilterOutput(vector<uint8_t> data);
+    void updateRecordOutput(vector<uint8_t> data);
     Result startFilterHandler();
+    Result startRecordFilterHandler();
+    void attachFilterToRecord(const sp<Dvr> dvr);
+    void detachFilterFromRecord();
 
   private:
     // Tuner service
     sp<Demux> mDemux;
+    // Dvr reference once the filter is attached to any
+    sp<Dvr> mDvr = nullptr;
     /**
      * Filter callbacks used on filter events or FMQ status
      */
@@ -99,6 +107,7 @@
     sp<IFilter> mDataSource;
     bool mIsDataSourceDemux = true;
     vector<uint8_t> mFilterOutput;
+    vector<uint8_t> mRecordFilterOutput;
     unique_ptr<FilterMQ> mFilterMQ;
     EventFlag* mFilterEventFlag;
     DemuxFilterEvent mFilterEvent;
@@ -120,6 +129,8 @@
      */
     const uint16_t SECTION_WRITE_COUNT = 10;
 
+    bool DEBUG_FILTER = false;
+
     /**
      * Filter handlers to handle the data filtering.
      * They are also responsible to write the filtered output into the filter FMQ
@@ -129,7 +140,6 @@
     Result startPesFilterHandler();
     Result startTsFilterHandler();
     Result startMediaFilterHandler();
-    Result startRecordFilterHandler();
     Result startPcrFilterHandler();
     Result startTemiFilterHandler();
     Result startFilterLoop();
@@ -165,6 +175,7 @@
     std::mutex mFilterStatusLock;
     std::mutex mFilterThreadLock;
     std::mutex mFilterOutputLock;
+    std::mutex mRecordFilterOutputLock;
 
     // temp handle single PES filter
     // TODO handle mulptiple Pes filters
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index f86b28d..c143d61 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -148,7 +148,7 @@
     uint32_t demuxId;
     if (it != mFrontendToDemux.end()) {
         demuxId = it->second;
-        mDemuxes[demuxId]->stopBroadcastInput();
+        mDemuxes[demuxId]->stopFrontendInput();
     }
 }
 
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
index ef33952..944da5a 100644
--- a/tv/tuner/1.0/types.hal
+++ b/tv/tuner/1.0/types.hal
@@ -2230,8 +2230,6 @@
 
         DemuxFilterSectionSettings section;
 
-        DemuxFilterPesDataSettings pesData;
-
         /**
          * true if the data from IP subtype go to next filter directly
          */
@@ -2248,7 +2246,7 @@
     /**
      * true if the filtered data is commpressed ip packet
      */
-    bool bIsCompressedIpPacket;
+    bool isCompressedIpPacket;
 
     safe_union FilterSettings {
         /**
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index da3e300..7977f25 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -65,6 +65,7 @@
 using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
 using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
 using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
 using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
 using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
 using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
@@ -95,6 +96,7 @@
 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;
 
@@ -379,7 +381,20 @@
 
 class DvrCallback : public IDvrCallback {
   public:
-    virtual Return<void> onRecordStatus(RecordStatus /*status*/) override { return Void(); }
+    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);
@@ -401,10 +416,17 @@
 
     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 {
@@ -412,22 +434,31 @@
         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>> mFilterIdToMQ;
     std::unique_ptr<FilterMQ> mPlaybackMQ;
+    std::unique_ptr<FilterMQ> mRecordMQ;
     std::map<uint32_t, EventFlag*> mFilterIdToMQEventFlag;
     std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
-    EventFlag* mPlaybackMQEventFlag;
 
     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;
 };
@@ -516,6 +547,92 @@
     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);
+}
+
 // Test environment for Tuner HIDL HAL.
 class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
   public:
@@ -555,6 +672,7 @@
     sp<DvrCallback> mDvrCallback;
     MQDesc mFilterMQDescriptor;
     MQDesc mPlaybackMQDescriptor;
+    MQDesc mRecordMQDescriptor;
     vector<uint32_t> mUsedFilterIds;
 
     uint32_t mDemuxId;
@@ -572,6 +690,8 @@
                                                        FrontendSettings settings);
     ::testing::AssertionResult getPlaybackMQDescriptor();
     ::testing::AssertionResult addPlaybackToDemux(PlaybackSettings setting);
+    ::testing::AssertionResult getRecordMQDescriptor();
+    ::testing::AssertionResult addRecordToDemux(RecordSettings setting);
     ::testing::AssertionResult addFilterToDemux(DemuxFilterType type, DemuxFilterSettings setting);
     ::testing::AssertionResult getFilterMQDescriptor();
     ::testing::AssertionResult closeDemux();
@@ -581,6 +701,9 @@
     ::testing::AssertionResult playbackDataFlowTest(vector<FilterConf> filterConf,
                                                     PlaybackConf playbackConf,
                                                     vector<string> goldenOutputFiles);
+    ::testing::AssertionResult recordDataFlowTest(vector<FilterConf> filterConf,
+                                                  RecordSettings recordSetting,
+                                                  vector<string> goldenOutputFiles);
     ::testing::AssertionResult broadcastDataFlowTest(vector<FilterConf> filterConf,
                                                      vector<string> goldenOutputFiles);
 };
@@ -766,6 +889,49 @@
     return ::testing::AssertionResult(status == Result::SUCCESS);
 }
 
+::testing::AssertionResult TunerHidlTest::addRecordToDemux(RecordSettings setting) {
+    Result status;
+
+    if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
+        return ::testing::AssertionFailure();
+    }
+
+    // Create dvr callback
+    mDvrCallback = new DvrCallback();
+
+    // Add playback input to the local demux
+    mDemux->openDvr(DvrType::RECORD, FMQ_SIZE_1M, mDvrCallback,
+                    [&](Result result, const sp<IDvr>& dvr) {
+                        mDvr = dvr;
+                        status = result;
+                    });
+
+    if (status != Result::SUCCESS) {
+        return ::testing::AssertionFailure();
+    }
+
+    DvrSettings dvrSetting;
+    dvrSetting.record(setting);
+    status = mDvr->configure(dvrSetting);
+
+    return ::testing::AssertionResult(status == Result::SUCCESS);
+}
+
+::testing::AssertionResult TunerHidlTest::getRecordMQDescriptor() {
+    Result status;
+
+    if ((!mDemux && createDemux() == ::testing::AssertionFailure()) || !mDvr) {
+        return ::testing::AssertionFailure();
+    }
+
+    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
+        mRecordMQDescriptor = dvrMQDesc;
+        status = result;
+    });
+
+    return ::testing::AssertionResult(status == Result::SUCCESS);
+}
+
 ::testing::AssertionResult TunerHidlTest::addFilterToDemux(DemuxFilterType type,
                                                            DemuxFilterSettings setting) {
     Result status;
@@ -997,6 +1163,82 @@
     return closeDemux();
 }
 
+::testing::AssertionResult TunerHidlTest::recordDataFlowTest(vector<FilterConf> filterConf,
+                                                             RecordSettings recordSetting,
+                                                             vector<string> /*goldenOutputFiles*/) {
+    Result status;
+    hidl_vec<FrontendId> feIds;
+
+    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+        status = result;
+        feIds = frontendIds;
+    });
+
+    if (feIds.size() == 0) {
+        ALOGW("[   WARN   ] Frontend isn't available");
+        return ::testing::AssertionFailure();
+    }
+
+    FrontendDvbtSettings dvbt{
+            .frequency = 1000,
+    };
+    FrontendSettings settings;
+    settings.dvbt(dvbt);
+
+    int filterIdsSize;
+    // Filter Configuration Module
+    for (int i = 0; i < filterConf.size(); i++) {
+        if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
+                    ::testing::AssertionFailure() ||
+            // TODO use a map to save the FMQs/EvenFlags and pass to callback
+            getFilterMQDescriptor() == ::testing::AssertionFailure()) {
+            return ::testing::AssertionFailure();
+        }
+        filterIdsSize = mUsedFilterIds.size();
+        mUsedFilterIds.resize(filterIdsSize + 1);
+        mUsedFilterIds[filterIdsSize] = mFilterId;
+        mFilters[mFilterId] = mFilter;
+    }
+
+    // Record Config Module
+    if (addRecordToDemux(recordSetting) == ::testing::AssertionFailure() ||
+        getRecordMQDescriptor() == ::testing::AssertionFailure()) {
+        return ::testing::AssertionFailure();
+    }
+    for (int i = 0; i <= filterIdsSize; i++) {
+        if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
+            return ::testing::AssertionFailure();
+        }
+    }
+
+    mDvrCallback->startRecordOutputThread(recordSetting, mRecordMQDescriptor);
+    status = mDvr->start();
+    if (status != Result::SUCCESS) {
+        return ::testing::AssertionFailure();
+    }
+
+    if (createDemuxWithFrontend(feIds[0], settings) != ::testing::AssertionSuccess()) {
+        return ::testing::AssertionFailure();
+    }
+
+    // Data Verify Module
+    mDvrCallback->testRecordOutput();
+
+    // Clean Up Module
+    for (int i = 0; i <= filterIdsSize; i++) {
+        if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
+            return ::testing::AssertionFailure();
+        }
+    }
+    if (mFrontend->stopTune() != Result::SUCCESS) {
+        return ::testing::AssertionFailure();
+    }
+    mUsedFilterIds.clear();
+    mFilterCallbacks.clear();
+    mFilters.clear();
+    return closeDemux();
+}
+
 /*
  * API STATUS TESTS
  */
@@ -1203,6 +1445,44 @@
     ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles));
 }
 
+TEST_F(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) {
+    description("Feed ts data from frontend to recording and test with ts record filter");
+
+    // todo modulize the filter conf parser
+    vector<FilterConf> filterConf;
+    filterConf.resize(1);
+
+    DemuxFilterSettings filterSetting;
+    DemuxTsFilterSettings tsFilterSetting{
+            .tpid = 119,
+    };
+    DemuxFilterRecordSettings recordFilterSetting;
+    tsFilterSetting.filterSettings.record(recordFilterSetting);
+    filterSetting.ts(tsFilterSetting);
+
+    DemuxFilterType type{
+            .mainType = DemuxFilterMainType::TS,
+    };
+    type.subType.tsFilterType(DemuxTsFilterType::RECORD);
+    FilterConf recordFilterConf{
+            .type = type,
+            .setting = filterSetting,
+    };
+    filterConf[0] = recordFilterConf;
+
+    RecordSettings recordSetting{
+            .statusMask = 0xf,
+            .lowThreshold = 0x1000,
+            .highThreshold = 0x07fff,
+            .dataFormat = DataFormat::TS,
+            .packetSize = 188,
+    };
+
+    vector<string> goldenOutputFiles;
+
+    ASSERT_TRUE(recordDataFlowTest(filterConf, recordSetting, goldenOutputFiles));
+}
+
 }  // namespace
 
 int main(int argc, char** argv) {