Merge "Use AidlMessageQueue for tuner service"
diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp
index 8d5c77f..ee7285d 100644
--- a/media/mediaserver/Android.bp
+++ b/media/mediaserver/Android.bp
@@ -17,6 +17,7 @@
     shared_libs: [
         "android.hardware.media.omx@1.0",
         "libandroidicu",
+        "libfmq",
         "libbinder",
         "libhidlbase",
         "liblog",
diff --git a/services/tuner/Android.bp b/services/tuner/Android.bp
index 65d8d41..5327289 100644
--- a/services/tuner/Android.bp
+++ b/services/tuner/Android.bp
@@ -40,6 +40,21 @@
     srcs: [
         ":tv_tuner_aidl",
     ],
+    imports: [
+        "android.hardware.common.fmq",
+    ],
+
+    backend: {
+        java: {
+            enabled: false,
+        },
+        cpp: {
+            enabled: false,
+        },
+        ndk: {
+            enabled: true,
+        },
+    },
 }
 
 cc_library {
@@ -52,8 +67,10 @@
 
     shared_libs: [
         "android.hardware.tv.tuner@1.0",
-        "libbinder",
+        "libbase",
         "libbinder_ndk",
+        "libcutils",
+        "libfmq",
         "libhidlbase",
         "liblog",
         "libmedia",
@@ -61,7 +78,13 @@
         "tv_tuner_aidl_interface-ndk_platform",
     ],
 
-    include_dirs: ["frameworks/av/include"],
+    static_libs: [
+        "android.hardware.common.fmq-unstable-ndk_platform",
+    ],
+
+    include_dirs: [
+      "frameworks/av/include"
+    ],
 
     cflags: [
         "-Werror",
@@ -83,6 +106,7 @@
         "android.hardware.tv.tuner@1.0",
         "libbase",
         "libbinder",
+        "libfmq",
         "liblog",
         "libtunerservice",
         "libutils",
diff --git a/services/tuner/TunerService.cpp b/services/tuner/TunerService.cpp
index 2b3de17..56cb34c 100644
--- a/services/tuner/TunerService.cpp
+++ b/services/tuner/TunerService.cpp
@@ -32,14 +32,17 @@
 using ::aidl::android::media::tv::tuner::TunerFrontendIsdbsCapabilities;
 using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtCapabilities;
 using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+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::DemuxTsFilterType;
 using ::android::hardware::tv::tuner::V1_0::FrontendId;
 using ::android::hardware::tv::tuner::V1_0::FrontendType;
 using ::android::hardware::tv::tuner::V1_0::Result;
 
 namespace android {
 
-sp<ITuner> TunerService::mTuner;
-
 TunerService::TunerService() {}
 TunerService::~TunerService() {}
 
@@ -47,17 +50,160 @@
     std::shared_ptr<TunerService> service =
             ::ndk::SharedRefBase::make<TunerService>();
     AServiceManager_addService(service->asBinder().get(), getServiceName());
+}
+
+template <typename HidlPayload, typename AidlPayload, typename AidlFlavor>
+bool TunerService::unsafeHidlToAidlMQDescriptor(
+        const hardware::MQDescriptor<HidlPayload, FlavorTypeToValue<AidlFlavor>::value>& hidlDesc,
+        MQDescriptor<AidlPayload, AidlFlavor>* aidlDesc) {
+    // TODO: use the builtin coversion method when it's merged.
+    ALOGD("unsafeHidlToAidlMQDescriptor");
+    static_assert(sizeof(HidlPayload) == sizeof(AidlPayload), "Payload types are incompatible");
+    static_assert(
+            has_typedef_fixed_size<AidlPayload>::value == true ||
+            std::is_fundamental<AidlPayload>::value ||
+            std::is_enum<AidlPayload>::value,
+            "Only fundamental types, enums, and AIDL parcelables annotated with @FixedSize "
+            "and built for the NDK backend are supported as AIDL payload types.");
+    aidlDesc->fileDescriptor = ndk::ScopedFileDescriptor(dup(hidlDesc.handle()->data[0]));
+    for (const auto& grantor : hidlDesc.grantors()) {
+        if (static_cast<int32_t>(grantor.offset) < 0 || static_cast<int64_t>(grantor.extent) < 0) {
+            ALOGD("Unsafe static_cast of grantor fields. offset=%d, extend=%ld",
+                    static_cast<int32_t>(grantor.offset), static_cast<long>(grantor.extent));
+            logError(
+                    "Unsafe static_cast of grantor fields. Either the hardware::MQDescriptor is "
+                    "invalid, or the MessageQueue is too large to be described by AIDL.");
+            return false;
+        }
+        aidlDesc->grantors.push_back(
+                GrantorDescriptor {
+                        .offset = static_cast<int32_t>(grantor.offset),
+                        .extent = static_cast<int64_t>(grantor.extent)
+                });
+    }
+    if (static_cast<int32_t>(hidlDesc.getQuantum()) < 0 ||
+            static_cast<int32_t>(hidlDesc.getFlags()) < 0) {
+        ALOGD("Unsafe static_cast of quantum or flags. Quantum=%d, flags=%d",
+                static_cast<int32_t>(hidlDesc.getQuantum()),
+                static_cast<int32_t>(hidlDesc.getFlags()));
+        logError(
+                "Unsafe static_cast of quantum or flags. Either the hardware::MQDescriptor is "
+                "invalid, or the MessageQueue is too large to be described by AIDL.");
+        return false;
+    }
+    aidlDesc->quantum = static_cast<int32_t>(hidlDesc.getQuantum());
+    aidlDesc->flags = static_cast<int32_t>(hidlDesc.getFlags());
+    return true;
+}
+
+bool TunerService::getITuner() {
+    ALOGD("getITuner");
+    if (mTuner != nullptr) {
+        return true;
+    }
     mTuner = ITuner::getService();
     if (mTuner == nullptr) {
-        ALOGE("Failed to get ITuner service.");
+        ALOGE("Failed to get ITuner service");
+        return false;
     }
+    return true;
+}
+
+Result TunerService::openDemux() {
+    ALOGD("openDemux");
+    if (!getITuner()) {
+        return Result::NOT_INITIALIZED;
+    }
+    if (mDemux != nullptr) {
+        return Result::SUCCESS;
+    }
+    Result res;
+    uint32_t id;
+    sp<IDemux> demuxSp;
+    mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) {
+        demuxSp = demux;
+        id = demuxId;
+        res = r;
+        ALOGD("open demux, id = %d", demuxId);
+    });
+    if (res == Result::SUCCESS) {
+        mDemux = demuxSp;
+    } else {
+        ALOGD("open demux failed, res = %d", res);
+    }
+    return res;
+}
+
+Result TunerService::openFilter() {
+    ALOGD("openFilter");
+    if (!getITuner()) {
+        return Result::NOT_INITIALIZED;
+    }
+    DemuxFilterMainType mainType = DemuxFilterMainType::TS;
+    DemuxFilterType filterType {
+        .mainType = mainType,
+    };
+    filterType.subType.tsFilterType(DemuxTsFilterType::VIDEO);
+
+    sp<FilterCallback> callback = new FilterCallback();
+    Result res;
+    mDemux->openFilter(filterType, 16000000, callback,
+            [&](Result r, const sp<IFilter>& filter) {
+                mFilter = filter;
+                res = r;
+            });
+    if (res != Result::SUCCESS || mFilter == NULL) {
+        ALOGD("Failed to open filter, type = %d", filterType.mainType);
+        return res;
+    }
+
+    return Result::SUCCESS;
+}
+
+Result TunerService::configFilter() {
+    ALOGD("configFilter");
+    if (mFilter == NULL) {
+        ALOGD("Failed to configure filter: filter not found");
+        return Result::NOT_INITIALIZED;
+    }
+    DemuxFilterSettings filterSettings;
+    DemuxTsFilterSettings tsFilterSettings {
+        .tpid = 256,
+    };
+    DemuxFilterAvSettings filterAvSettings {
+        .isPassthrough = false,
+    };
+    tsFilterSettings.filterSettings.av(filterAvSettings);
+    filterSettings.ts(tsFilterSettings);
+    Result res = mFilter->configure(filterSettings);
+
+    if (res != Result::SUCCESS) {
+        ALOGD("config filter failed, res = %d", res);
+        return res;
+    }
+
+    Result getQueueDescResult = Result::UNKNOWN_ERROR;
+    mFilter->getQueueDesc(
+            [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
+                mFilterMQDesc = desc;
+                getQueueDescResult = r;
+                ALOGD("getFilterQueueDesc");
+            });
+    if (getQueueDescResult == Result::SUCCESS) {
+        unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
+                mFilterMQDesc,  &mAidlMQDesc);
+        mAidlMq = new (std::nothrow) AidlMessageQueue(mAidlMQDesc);
+        EventFlag::createEventFlag(mAidlMq->getEventFlagWord(), &mEventFlag);
+    } else {
+        ALOGD("get MQDesc failed, res = %d", getQueueDescResult);
+    }
+    return getQueueDescResult;
 }
 
 Status TunerService::getFrontendIds(std::vector<int32_t>* ids, int32_t* /* _aidl_return */) {
-    if (mTuner == nullptr) {
-        ALOGE("ITuner service is not init.");
+    if (!getITuner()) {
         return ::ndk::ScopedAStatus::fromServiceSpecificError(
-                static_cast<int32_t>(Result::UNAVAILABLE));
+                static_cast<int32_t>(Result::NOT_INITIALIZED));
     }
     hidl_vec<FrontendId> feIds;
     Result res;
@@ -221,4 +367,24 @@
     info.caps = caps;
     return info;
 }
+
+Status TunerService::getFmqSyncReadWrite(
+        MQDescriptor<int8_t, SynchronizedReadWrite>* mqDesc, bool* _aidl_return) {
+    ALOGD("getFmqSyncReadWrite");
+    // TODO: put the following methods AIDL, and should be called from clients.
+    openDemux();
+    openFilter();
+    configFilter();
+    mFilter->start();
+    if (mqDesc == nullptr) {
+        ALOGD("getFmqSyncReadWrite null MQDescriptor.");
+        *_aidl_return = false;
+    } else {
+        ALOGD("getFmqSyncReadWrite true");
+        *_aidl_return = true;
+        *mqDesc = std::move(mAidlMQDesc);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
 } // namespace android
diff --git a/services/tuner/TunerService.h b/services/tuner/TunerService.h
index 36ccd3e..26591ab 100644
--- a/services/tuner/TunerService.h
+++ b/services/tuner/TunerService.h
@@ -20,17 +20,59 @@
 #include <aidl/android/media/tv/tuner/BnTunerService.h>
 #include <aidl/android/media/tv/tuner/TunerServiceFrontendInfo.h>
 #include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <fmq/AidlMessageQueue.h>
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
 
-using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::GrantorDescriptor;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
 using ::aidl::android::media::tv::tuner::BnTunerService;
 using ::aidl::android::media::tv::tuner::ITunerFrontend;
 using ::aidl::android::media::tv::tuner::TunerServiceFrontendInfo;
+
+using ::android::hardware::details::logError;
+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::DemuxFilterAvSettings;
+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::DemuxFilterStatus;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::FrontendId;
 using ::android::hardware::tv::tuner::V1_0::FrontendInfo;
+using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IFilter;
+using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
 using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+using Status = ::ndk::ScopedAStatus;
 
 namespace android {
 
+
+struct FilterCallback : public IFilterCallback {
+    ~FilterCallback() {}
+    Return<void> onFilterEvent(const DemuxFilterEvent&) {
+        return Void();
+    }
+    Return<void> onFilterStatus(const DemuxFilterStatus) {
+        return Void();
+    }
+};
+
 class TunerService : public BnTunerService {
+    typedef AidlMessageQueue<int8_t, SynchronizedReadWrite> AidlMessageQueue;
+    typedef MessageQueue<uint8_t, kSynchronizedReadWrite> HidlMessageQueue;
+    typedef MQDescriptor<int8_t, SynchronizedReadWrite> AidlMQDesc;
 
 public:
     static char const *getServiceName() { return "media.tuner"; }
@@ -46,10 +88,27 @@
     Status getFrontendInfo(int32_t frontendHandle, TunerServiceFrontendInfo* _aidl_return) override;
     Status openFrontend(
             int32_t frontendHandle, std::shared_ptr<ITunerFrontend>* _aidl_return) override;
+    Status getFmqSyncReadWrite(
+            MQDescriptor<int8_t, SynchronizedReadWrite>* mqDesc, bool* _aidl_return) override;
 
 private:
-    static sp<ITuner> mTuner;
+    template <typename HidlPayload, typename AidlPayload, typename AidlFlavor>
+    bool unsafeHidlToAidlMQDescriptor(
+            const hardware::MQDescriptor<HidlPayload, FlavorTypeToValue<AidlFlavor>::value>& hidl,
+            MQDescriptor<AidlPayload, AidlFlavor>* aidl);
 
+    bool getITuner();
+    Result openFilter();
+    Result openDemux();
+    Result configFilter();
+
+    sp<ITuner> mTuner;
+    sp<IDemux> mDemux;
+    sp<IFilter> mFilter;
+    AidlMessageQueue* mAidlMq;
+    MQDescriptorSync<uint8_t> mFilterMQDesc;
+    AidlMQDesc mAidlMQDesc;
+    EventFlag* mEventFlag;
     TunerServiceFrontendInfo convertToAidlFrontendInfo(int feId, FrontendInfo halInfo);
 };
 
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
index 5a0b47d..5c1bce7 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
@@ -16,6 +16,9 @@
 
 package android.media.tv.tuner;
 
+import android.hardware.common.fmq.MQDescriptor;
+import android.hardware.common.fmq.SynchronizedReadWrite;
+import android.hardware.common.fmq.UnsynchronizedWrite;
 import android.media.tv.tuner.ITunerFrontend;
 import android.media.tv.tuner.TunerServiceFrontendInfo;
 
@@ -24,6 +27,7 @@
  *
  * {@hide}
  */
+//@VintfStability
 interface ITunerService {
 
     /**
@@ -48,4 +52,11 @@
      * @return the aidl interface of the frontend.
      */
     ITunerFrontend openFrontend(in int frontendHandle);
+
+    /*
+     * Gets synchronized fast message queue.
+     *
+     * @return true if succeeds, false otherwise.
+     */
+    boolean getFmqSyncReadWrite(out MQDescriptor<byte, SynchronizedReadWrite> mqDesc);
 }