/**
 * Copyright (c) 2021, 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.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "TunerHidlService"

#include "TunerHidlService.h"

#include <aidl/android/hardware/tv/tuner/FrontendIsdbtTimeInterleaveMode.h>
#include <aidl/android/hardware/tv/tuner/Result.h>
#include <android/binder_manager.h>
#include <binder/IPCThreadState.h>
#include <binder/PermissionCache.h>
#include <cutils/properties.h>
#include <utils/Log.h>

#include "TunerHelper.h"
#include "TunerHidlDemux.h"
#include "TunerHidlDescrambler.h"
#include "TunerHidlFrontend.h"
#include "TunerHidlLnb.h"

using ::aidl::android::hardware::tv::tuner::FrontendAnalogCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Capabilities;
using ::aidl::android::hardware::tv::tuner::FrontendAtscCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendDtmbCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendDvbcCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendDvbsCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendDvbtCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Capabilities;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbsCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtTimeInterleaveMode;
using ::aidl::android::hardware::tv::tuner::FrontendType;
using ::aidl::android::hardware::tv::tuner::Result;
using ::android::IPCThreadState;
using ::android::PermissionCache;
using ::android::hardware::hidl_vec;

using HidlFrontendId = ::android::hardware::tv::tuner::V1_0::FrontendId;
using HidlLnbId = ::android::hardware::tv::tuner::V1_0::LnbId;
using HidlFrontendType = ::android::hardware::tv::tuner::V1_1::FrontendType;

using namespace std;

namespace aidl {
namespace android {
namespace media {
namespace tv {
namespace tuner {

TunerHidlService::TunerHidlService() {
    mTuner = HidlITuner::getService();
    ALOGE_IF(mTuner == nullptr, "Failed to get ITuner service");
    mTunerVersion = TUNER_HAL_VERSION_1_0;

    mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::castFrom(mTuner);
    if (mTuner_1_1 != nullptr) {
        mTunerVersion = TUNER_HAL_VERSION_1_1;
    } else {
        ALOGD("Failed to get ITuner_1_1 service");
    }

    // Register tuner resources to TRM.
    updateTunerResources();
}

TunerHidlService::~TunerHidlService() {
    mOpenedFrontends.clear();
    mLnaStatus = -1;
    mTuner = nullptr;
    mTuner_1_1 = nullptr;
}

binder_status_t TunerHidlService::instantiate() {
    if (HidlITuner::getService() == nullptr) {
        ALOGD("Failed to get ITuner HIDL HAL");
        return STATUS_NAME_NOT_FOUND;
    }

    shared_ptr<TunerHidlService> tunerService = ::ndk::SharedRefBase::make<TunerHidlService>();
    bool lazyHal = property_get_bool("ro.tuner.lazyhal", false);
    if (lazyHal) {
        return AServiceManager_registerLazyService(tunerService->asBinder().get(),
                                                   getServiceName());
    }
    return AServiceManager_addService(tunerService->asBinder().get(), getServiceName());
}

::ndk::ScopedAStatus TunerHidlService::openDemux(int64_t /* in_demuxHandle */,
                                                 shared_ptr<ITunerDemux>* _aidl_return) {
    ALOGV("openDemux");
    HidlResult res;
    uint32_t id;
    sp<IDemux> demuxSp = nullptr;
    mTuner->openDemux([&](HidlResult r, uint32_t demuxId, const sp<IDemux>& demux) {
        demuxSp = demux;
        id = demuxId;
        res = r;
        ALOGD("open demux, id = %d", demuxId);
    });
    if (res == HidlResult::SUCCESS) {
        *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlDemux>(demuxSp, id,
                                                                   this->ref<TunerHidlService>());
        return ::ndk::ScopedAStatus::ok();
    }

    ALOGW("open demux failed, res = %d", res);
    return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}

::ndk::ScopedAStatus TunerHidlService::getDemuxInfo(int64_t /* in_demuxHandle */,
                                                    DemuxInfo* /* _aidl_return */) {
    ALOGE("getDemuxInfo is not supported");
    return ::ndk::ScopedAStatus::fromServiceSpecificError(
            static_cast<int32_t>(HidlResult::UNAVAILABLE));
}

::ndk::ScopedAStatus TunerHidlService::getDemuxInfoList(
        vector<DemuxInfo>* /* _aidle_return */) {
    ALOGE("getDemuxInfoList is not supported");
    return ::ndk::ScopedAStatus::fromServiceSpecificError(
            static_cast<int32_t>(HidlResult::UNAVAILABLE));
}

::ndk::ScopedAStatus TunerHidlService::getDemuxCaps(DemuxCapabilities* _aidl_return) {
    ALOGV("getDemuxCaps");
    HidlResult res;
    HidlDemuxCapabilities caps;
    mTuner->getDemuxCaps([&](HidlResult r, const HidlDemuxCapabilities& demuxCaps) {
        caps = demuxCaps;
        res = r;
    });
    if (res == HidlResult::SUCCESS) {
        *_aidl_return = getAidlDemuxCaps(caps);
        return ::ndk::ScopedAStatus::ok();
    }

    ALOGW("Get demux caps failed, res = %d", res);
    return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
}

::ndk::ScopedAStatus TunerHidlService::getFrontendIds(vector<int32_t>* ids) {
    hidl_vec<HidlFrontendId> feIds;
    HidlResult res = getHidlFrontendIds(feIds);
    if (res != HidlResult::SUCCESS) {
        return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
    }
    ids->resize(feIds.size());
    copy(feIds.begin(), feIds.end(), ids->begin());

    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::getFrontendInfo(int32_t id, FrontendInfo* _aidl_return) {
    HidlFrontendInfo info;
    HidlResult res = getHidlFrontendInfo(id, info);
    if (res != HidlResult::SUCCESS) {
        return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
    }

    HidlFrontendDtmbCapabilities dtmbCaps;
    if (static_cast<HidlFrontendType>(info.type) == HidlFrontendType::DTMB) {
        if (mTuner_1_1 == nullptr) {
            ALOGE("ITuner_1_1 service is not init.");
            return ::ndk::ScopedAStatus::fromServiceSpecificError(
                    static_cast<int32_t>(Result::UNAVAILABLE));
        }

        mTuner_1_1->getFrontendDtmbCapabilities(
                id, [&](HidlResult r, const HidlFrontendDtmbCapabilities& caps) {
                    dtmbCaps = caps;
                    res = r;
                });
        if (res != HidlResult::SUCCESS) {
            return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
        }
    }

    *_aidl_return = getAidlFrontendInfo(info, dtmbCaps);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::openFrontend(int64_t frontendHandle,
                                                    shared_ptr<ITunerFrontend>* _aidl_return) {
    HidlResult status;
    sp<HidlIFrontend> frontend;
    int id = TunerHelper::getResourceIdFromHandle(frontendHandle, FRONTEND);
    mTuner->openFrontendById(id, [&](HidlResult result, const sp<HidlIFrontend>& fe) {
        frontend = fe;
        status = result;
    });
    if (status != HidlResult::SUCCESS) {
        return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
    }

    shared_ptr<TunerHidlFrontend> tunerFrontend = ::ndk::SharedRefBase::make<TunerHidlFrontend>(
            frontend, id, this->ref<TunerHidlService>());
    if (mLnaStatus != -1) {
        tunerFrontend->setLna(mLnaStatus == 1);
    }
    {
        Mutex::Autolock _l(mOpenedFrontendsLock);
        mOpenedFrontends.insert(tunerFrontend);
    }
    *_aidl_return = tunerFrontend;
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::openLnb(int64_t lnbHandle,
                                               shared_ptr<ITunerLnb>* _aidl_return) {
    HidlResult status;
    sp<HidlILnb> lnb;
    int id = TunerHelper::getResourceIdFromHandle(lnbHandle, LNB);
    mTuner->openLnbById(id, [&](HidlResult result, const sp<HidlILnb>& lnbSp) {
        lnb = lnbSp;
        status = result;
    });
    if (status != HidlResult::SUCCESS) {
        return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
    }

    *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlLnb>(lnb, id);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::openLnbByName(const string& lnbName,
                                                     shared_ptr<ITunerLnb>* _aidl_return) {
    int lnbId;
    HidlResult status;
    sp<HidlILnb> lnb;
    mTuner->openLnbByName(lnbName, [&](HidlResult r, HidlLnbId id, const sp<HidlILnb>& lnbSp) {
        status = r;
        lnb = lnbSp;
        lnbId = static_cast<int32_t>(id);
    });
    if (status != HidlResult::SUCCESS) {
        return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
    }

    *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlLnb>(lnb, lnbId);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::openDescrambler(
        int64_t /*descramblerHandle*/, shared_ptr<ITunerDescrambler>* _aidl_return) {
    HidlResult status;
    sp<HidlIDescrambler> descrambler;
    //int id = TunerHelper::getResourceIdFromHandle(descramblerHandle, DESCRAMBLER);
    mTuner->openDescrambler([&](HidlResult r, const sp<HidlIDescrambler>& descramblerSp) {
        status = r;
        descrambler = descramblerSp;
    });
    if (status != HidlResult::SUCCESS) {
        return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
    }

    *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlDescrambler>(descrambler);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::getTunerHalVersion(int* _aidl_return) {
    *_aidl_return = mTunerVersion;
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::openSharedFilter(
        const string& in_filterToken, const shared_ptr<ITunerFilterCallback>& in_cb,
        shared_ptr<ITunerFilter>* _aidl_return) {
    if (mTuner == nullptr) {
        ALOGE("get ITuner failed");
        return ::ndk::ScopedAStatus::fromServiceSpecificError(
                static_cast<int32_t>(Result::UNAVAILABLE));
    }

    if (!PermissionCache::checkCallingPermission(sSharedFilterPermission)) {
        ALOGE("Request requires android.permission.ACCESS_TV_SHARED_FILTER");
        return ::ndk::ScopedAStatus::fromServiceSpecificError(
                static_cast<int32_t>(Result::UNAVAILABLE));
    }

    Mutex::Autolock _l(mSharedFiltersLock);
    if (mSharedFilters.find(in_filterToken) == mSharedFilters.end()) {
        *_aidl_return = nullptr;
        ALOGD("fail to find %s", in_filterToken.c_str());
        return ::ndk::ScopedAStatus::fromServiceSpecificError(
                static_cast<int32_t>(Result::INVALID_STATE));
    }

    shared_ptr<TunerHidlFilter> filter = mSharedFilters.at(in_filterToken);
    IPCThreadState* ipc = IPCThreadState::self();
    const int pid = ipc->getCallingPid();
    if (!filter->isSharedFilterAllowed(pid)) {
        *_aidl_return = nullptr;
        ALOGD("shared filter %s is opened in the same process", in_filterToken.c_str());
        return ::ndk::ScopedAStatus::fromServiceSpecificError(
                static_cast<int32_t>(Result::INVALID_STATE));
    }

    filter->attachSharedFilterCallback(in_cb);

    *_aidl_return = filter;
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::isLnaSupported(bool* /* _aidl_return */) {
    return ::ndk::ScopedAStatus::fromServiceSpecificError(
            static_cast<int32_t>(Result::UNAVAILABLE));
}

::ndk::ScopedAStatus TunerHidlService::setLna(bool bEnable) {
    if (mTuner == nullptr) {
        ALOGE("get ITuner failed");
        return ::ndk::ScopedAStatus::fromServiceSpecificError(
                static_cast<int32_t>(Result::UNAVAILABLE));
    }

    mLnaStatus = bEnable ? 1 : 0;

    {
        Mutex::Autolock _l(mOpenedFrontendsLock);
        for (auto it = mOpenedFrontends.begin(); it != mOpenedFrontends.end(); ++it) {
            (*it)->setLna(mLnaStatus == 1);
        }
    }

    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TunerHidlService::setMaxNumberOfFrontends(FrontendType /* in_frontendType */,
                                                               int32_t /* in_maxNumber */) {
    return ::ndk::ScopedAStatus::fromServiceSpecificError(
            static_cast<int32_t>(Result::UNAVAILABLE));
}

::ndk::ScopedAStatus TunerHidlService::getMaxNumberOfFrontends(FrontendType /* in_frontendType */,
                                                               int32_t* _aidl_return) {
    *_aidl_return = -1;
    return ::ndk::ScopedAStatus::fromServiceSpecificError(
            static_cast<int32_t>(Result::UNAVAILABLE));
}

string TunerHidlService::addFilterToShared(const shared_ptr<TunerHidlFilter>& sharedFilter) {
    Mutex::Autolock _l(mSharedFiltersLock);

    // Use sharedFilter address as token.
    string token = to_string(reinterpret_cast<std::uintptr_t>(sharedFilter.get()));
    mSharedFilters[token] = sharedFilter;

    return token;
}

void TunerHidlService::removeSharedFilter(const shared_ptr<TunerHidlFilter>& sharedFilter) {
    Mutex::Autolock _l(mSharedFiltersLock);

    // Use sharedFilter address as token.
    mSharedFilters.erase(to_string(reinterpret_cast<std::uintptr_t>(sharedFilter.get())));
}

void TunerHidlService::removeFrontend(const shared_ptr<TunerHidlFrontend>& frontend) {
    Mutex::Autolock _l(mOpenedFrontendsLock);
    for (auto it = mOpenedFrontends.begin(); it != mOpenedFrontends.end(); ++it) {
        if (it->get() == frontend.get()) {
            mOpenedFrontends.erase(it);
            break;
        }
    }
}

void TunerHidlService::updateTunerResources() {
    TunerHelper::updateTunerResources(getTRMFrontendInfos(), getTRMLnbHandles());
}

vector<TunerFrontendInfo> TunerHidlService::getTRMFrontendInfos() {
    vector<TunerFrontendInfo> infos;
    hidl_vec<HidlFrontendId> ids;
    HidlResult res = getHidlFrontendIds(ids);
    if (res != HidlResult::SUCCESS) {
        return infos;
    }

    for (int i = 0; i < ids.size(); i++) {
        HidlFrontendInfo frontendInfo;
        HidlResult res = getHidlFrontendInfo(static_cast<int32_t>(ids[i]), frontendInfo);
        if (res != HidlResult::SUCCESS) {
            continue;
        }
        TunerFrontendInfo tunerFrontendInfo{
                .handle = TunerHelper::getResourceHandleFromId(static_cast<int32_t>(ids[i]),
                                                               FRONTEND),
                .type = static_cast<int32_t>(frontendInfo.type),
                .exclusiveGroupId = static_cast<int32_t>(frontendInfo.exclusiveGroupId),
        };
        infos.push_back(tunerFrontendInfo);
    }

    return infos;
}

vector<int64_t> TunerHidlService::getTRMLnbHandles() {
    vector<int64_t> lnbHandles;
    if (mTuner != nullptr) {
        HidlResult res;
        vector<HidlLnbId> lnbIds;
        mTuner->getLnbIds([&](HidlResult r, const hidl_vec<HidlLnbId>& ids) {
            lnbIds = ids;
            res = r;
        });
        if (res == HidlResult::SUCCESS && lnbIds.size() > 0) {
            for (int i = 0; i < lnbIds.size(); i++) {
                lnbHandles.push_back(
                        TunerHelper::getResourceHandleFromId(static_cast<int32_t>(lnbIds[i]), LNB));
            }
        }
    }

    return lnbHandles;
}

HidlResult TunerHidlService::getHidlFrontendIds(hidl_vec<HidlFrontendId>& ids) {
    if (mTuner == nullptr) {
        return HidlResult::NOT_INITIALIZED;
    }
    HidlResult res;
    mTuner->getFrontendIds([&](HidlResult r, const hidl_vec<HidlFrontendId>& frontendIds) {
        ids = frontendIds;
        res = r;
    });
    return res;
}

HidlResult TunerHidlService::getHidlFrontendInfo(const int id, HidlFrontendInfo& info) {
    if (mTuner == nullptr) {
        return HidlResult::NOT_INITIALIZED;
    }
    HidlResult res;
    mTuner->getFrontendInfo(id, [&](HidlResult r, const HidlFrontendInfo& feInfo) {
        info = feInfo;
        res = r;
    });
    return res;
}

DemuxCapabilities TunerHidlService::getAidlDemuxCaps(const HidlDemuxCapabilities& caps) {
    DemuxCapabilities aidlCaps{
            .numDemux = static_cast<int32_t>(caps.numDemux),
            .numRecord = static_cast<int32_t>(caps.numRecord),
            .numPlayback = static_cast<int32_t>(caps.numPlayback),
            .numTsFilter = static_cast<int32_t>(caps.numTsFilter),
            .numSectionFilter = static_cast<int32_t>(caps.numSectionFilter),
            .numAudioFilter = static_cast<int32_t>(caps.numAudioFilter),
            .numVideoFilter = static_cast<int32_t>(caps.numVideoFilter),
            .numPesFilter = static_cast<int32_t>(caps.numPesFilter),
            .numPcrFilter = static_cast<int32_t>(caps.numPcrFilter),
            .numBytesInSectionFilter = static_cast<int64_t>(caps.numBytesInSectionFilter),
            .filterCaps = static_cast<int32_t>(caps.filterCaps),
            .bTimeFilter = caps.bTimeFilter,
    };
    aidlCaps.linkCaps.resize(caps.linkCaps.size());
    copy(caps.linkCaps.begin(), caps.linkCaps.end(), aidlCaps.linkCaps.begin());
    return aidlCaps;
}

FrontendInfo TunerHidlService::getAidlFrontendInfo(
        const HidlFrontendInfo& halInfo, const HidlFrontendDtmbCapabilities& halDtmbCaps) {
    FrontendInfo info{
            .type = static_cast<FrontendType>(halInfo.type),
            .minFrequency = static_cast<int64_t>(halInfo.minFrequency),
            .maxFrequency = static_cast<int64_t>(halInfo.maxFrequency),
            .minSymbolRate = static_cast<int32_t>(halInfo.minSymbolRate),
            .maxSymbolRate = static_cast<int32_t>(halInfo.maxSymbolRate),
            .acquireRange = static_cast<int64_t>(halInfo.acquireRange),
            .exclusiveGroupId = static_cast<int32_t>(halInfo.exclusiveGroupId),
    };
    for (int i = 0; i < halInfo.statusCaps.size(); i++) {
        info.statusCaps.push_back(static_cast<FrontendStatusType>(halInfo.statusCaps[i]));
    }

    FrontendCapabilities caps;
    switch (halInfo.type) {
    case ::android::hardware::tv::tuner::V1_0::FrontendType::ANALOG: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::analogCaps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendAnalogCapabilities analogCaps{
                    .typeCap = static_cast<int32_t>(halInfo.frontendCaps.analogCaps().typeCap),
                    .sifStandardCap =
                            static_cast<int32_t>(halInfo.frontendCaps.analogCaps().sifStandardCap),
            };
            caps.set<FrontendCapabilities::analogCaps>(analogCaps);
        }
        break;
    }
    case ::android::hardware::tv::tuner::V1_0::FrontendType::ATSC: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::atscCaps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendAtscCapabilities atscCaps{
                    .modulationCap =
                            static_cast<int32_t>(halInfo.frontendCaps.atscCaps().modulationCap),
            };
            caps.set<FrontendCapabilities::atscCaps>(atscCaps);
        }
        break;
    }
    case ::android::hardware::tv::tuner::V1_0::FrontendType::ATSC3: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::atsc3Caps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendAtsc3Capabilities atsc3Caps{
                    .bandwidthCap =
                            static_cast<int32_t>(halInfo.frontendCaps.atsc3Caps().bandwidthCap),
                    .modulationCap =
                            static_cast<int32_t>(halInfo.frontendCaps.atsc3Caps().modulationCap),
                    .timeInterleaveModeCap = static_cast<int32_t>(
                            halInfo.frontendCaps.atsc3Caps().timeInterleaveModeCap),
                    .codeRateCap =
                            static_cast<int32_t>(halInfo.frontendCaps.atsc3Caps().codeRateCap),
                    .demodOutputFormatCap = static_cast<int8_t>(
                            halInfo.frontendCaps.atsc3Caps().demodOutputFormatCap),
                    .fecCap = static_cast<int32_t>(halInfo.frontendCaps.atsc3Caps().fecCap),
            };
            caps.set<FrontendCapabilities::atsc3Caps>(atsc3Caps);
        }
        break;
    }
    case ::android::hardware::tv::tuner::V1_0::FrontendType::DVBC: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::dvbcCaps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendDvbcCapabilities dvbcCaps{
                    .modulationCap =
                            static_cast<int32_t>(halInfo.frontendCaps.dvbcCaps().modulationCap),
                    .fecCap = static_cast<int64_t>(halInfo.frontendCaps.dvbcCaps().fecCap),
                    .annexCap = static_cast<int8_t>(halInfo.frontendCaps.dvbcCaps().annexCap),
            };
            caps.set<FrontendCapabilities::dvbcCaps>(dvbcCaps);
        }
        break;
    }
    case ::android::hardware::tv::tuner::V1_0::FrontendType::DVBS: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::dvbsCaps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendDvbsCapabilities dvbsCaps{
                    .modulationCap =
                            static_cast<int32_t>(halInfo.frontendCaps.dvbsCaps().modulationCap),
                    .innerfecCap =
                            static_cast<int64_t>(halInfo.frontendCaps.dvbsCaps().innerfecCap),
                    .standard = static_cast<int8_t>(halInfo.frontendCaps.dvbsCaps().standard),
            };
            caps.set<FrontendCapabilities::dvbsCaps>(dvbsCaps);
        }
        break;
    }
    case ::android::hardware::tv::tuner::V1_0::FrontendType::DVBT: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::dvbtCaps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendDvbtCapabilities dvbtCaps{
                    .transmissionModeCap = static_cast<int32_t>(
                            halInfo.frontendCaps.dvbtCaps().transmissionModeCap),
                    .bandwidthCap =
                            static_cast<int32_t>(halInfo.frontendCaps.dvbtCaps().bandwidthCap),
                    .constellationCap =
                            static_cast<int32_t>(halInfo.frontendCaps.dvbtCaps().constellationCap),
                    .coderateCap =
                            static_cast<int32_t>(halInfo.frontendCaps.dvbtCaps().coderateCap),
                    .hierarchyCap =
                            static_cast<int32_t>(halInfo.frontendCaps.dvbtCaps().hierarchyCap),
                    .guardIntervalCap =
                            static_cast<int32_t>(halInfo.frontendCaps.dvbtCaps().guardIntervalCap),
                    .isT2Supported = halInfo.frontendCaps.dvbtCaps().isT2Supported,
                    .isMisoSupported = halInfo.frontendCaps.dvbtCaps().isMisoSupported,
            };
            caps.set<FrontendCapabilities::dvbtCaps>(dvbtCaps);
        }
        break;
    }
    case ::android::hardware::tv::tuner::V1_0::FrontendType::ISDBS: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::isdbsCaps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendIsdbsCapabilities isdbsCaps{
                    .modulationCap =
                            static_cast<int32_t>(halInfo.frontendCaps.isdbsCaps().modulationCap),
                    .coderateCap =
                            static_cast<int32_t>(halInfo.frontendCaps.isdbsCaps().coderateCap),
            };
            caps.set<FrontendCapabilities::isdbsCaps>(isdbsCaps);
        }
        break;
    }
    case ::android::hardware::tv::tuner::V1_0::FrontendType::ISDBS3: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::isdbs3Caps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendIsdbs3Capabilities isdbs3Caps{
                    .modulationCap =
                            static_cast<int32_t>(halInfo.frontendCaps.isdbs3Caps().modulationCap),
                    .coderateCap =
                            static_cast<int32_t>(halInfo.frontendCaps.isdbs3Caps().coderateCap),
            };
            caps.set<FrontendCapabilities::isdbs3Caps>(isdbs3Caps);
        }
        break;
    }
    case ::android::hardware::tv::tuner::V1_0::FrontendType::ISDBT: {
        if (HidlFrontendInfo::FrontendCapabilities::hidl_discriminator::isdbtCaps ==
            halInfo.frontendCaps.getDiscriminator()) {
            FrontendIsdbtCapabilities isdbtCaps{
                    .modeCap = static_cast<int32_t>(halInfo.frontendCaps.isdbtCaps().modeCap),
                    .bandwidthCap =
                            static_cast<int32_t>(halInfo.frontendCaps.isdbtCaps().bandwidthCap),
                    .modulationCap =
                            static_cast<int32_t>(halInfo.frontendCaps.isdbtCaps().modulationCap),
                    .coderateCap =
                            static_cast<int32_t>(halInfo.frontendCaps.isdbtCaps().coderateCap),
                    .guardIntervalCap =
                            static_cast<int32_t>(halInfo.frontendCaps.isdbtCaps().guardIntervalCap),
                    .timeInterleaveCap =
                            static_cast<int32_t>(FrontendIsdbtTimeInterleaveMode::UNDEFINED),
                    .isSegmentAuto = false,
                    .isFullSegment = false,
            };
            caps.set<FrontendCapabilities::isdbtCaps>(isdbtCaps);
        }
        break;
    }
    default: {
        if (static_cast<HidlFrontendType>(info.type) == HidlFrontendType::DTMB) {
            FrontendDtmbCapabilities dtmbCaps{
                    .transmissionModeCap = static_cast<int32_t>(halDtmbCaps.transmissionModeCap),
                    .bandwidthCap = static_cast<int32_t>(halDtmbCaps.bandwidthCap),
                    .modulationCap = static_cast<int32_t>(halDtmbCaps.modulationCap),
                    .codeRateCap = static_cast<int32_t>(halDtmbCaps.codeRateCap),
                    .guardIntervalCap = static_cast<int32_t>(halDtmbCaps.guardIntervalCap),
                    .interleaveModeCap = static_cast<int32_t>(halDtmbCaps.interleaveModeCap),
            };
            caps.set<FrontendCapabilities::dtmbCaps>(dtmbCaps);
        }
        break;
    }
    }

    info.frontendCaps = caps;
    return info;
}

}  // namespace tuner
}  // namespace tv
}  // namespace media
}  // namespace android
}  // namespace aidl
