Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46fcbd69 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "tuner_api_filter_read"

* changes:
  Add read methods for filter
  Add a cpp filter wraper
  Filter settings and configure()
parents 9d734ff4 964e6137
Loading
Loading
Loading
Loading
+383 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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.
 */

package android.media.tv.tuner;

import android.annotation.Nullable;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.TunerConstants.FilterSettingsType;

import java.util.List;

/**
 * Demux Filter settings.
 *
 * @hide
 */
public abstract class FilterSettings {
    @Nullable
    protected final Settings mSettings;

    protected FilterSettings(Settings settings) {
        mSettings = settings;
    }

    /**
     * Gets filter settings type
     */
    @FilterSettingsType public abstract int getType();

    // TODO: more builders and getters

    /**
     *  Filter Settings for a TS filter.
     */
    public static class TsFilterSettings extends FilterSettings {
        private int mTpid;

        private TsFilterSettings(Settings settings, int tpid) {
            super(settings);
            mTpid = tpid;
        }

        @Override
        public int getType() {
            return TunerConstants.FILTER_SETTINGS_TS;
        }

        /**
         * Creates a new builder.
         */
        public static Builder newBuilder() {
            return new Builder();
        }

        /**
         * Builder for TsFilterSettings.
         */
        public static class Builder {
            private Settings mSettings;
            private int mTpid;

            /**
             * Sets settings.
             */
            public Builder setSettings(Settings settings) {
                mSettings = settings;
                return this;
            }

            /**
             * Sets TPID.
             */
            public Builder setTpid(int tpid) {
                mTpid = tpid;
                return this;
            }

            /**
             * Builds a TsFilterSettings instance.
             */
            public TsFilterSettings build() {
                return new TsFilterSettings(mSettings, mTpid);
            }
        }
    }

    /**
     *  Filter Settings for a MMTP filter.
     */
    public static class MmtpFilterSettings extends FilterSettings {
        private int mMmtpPid;

        public MmtpFilterSettings(Settings settings) {
            super(settings);
        }

        @Override
        public int getType() {
            return TunerConstants.FILTER_SETTINGS_MMTP;
        }
    }


    /**
     *  Filter Settings for a IP filter.
     */
    public static class IpFilterSettings extends FilterSettings {
        private byte[] mSrcIpAddress;
        private byte[] mDstIpAddress;
        private int mSrcPort;
        private int mDstPort;
        private boolean mPassthrough;

        public IpFilterSettings(Settings settings) {
            super(settings);
        }

        @Override
        public int getType() {
            return TunerConstants.FILTER_SETTINGS_IP;
        }
    }


    /**
     *  Filter Settings for a TLV filter.
     */
    public static class TlvFilterSettings extends FilterSettings {
        private int mPacketType;
        private boolean mIsCompressedIpPacket;
        private boolean mPassthrough;

        public TlvFilterSettings(Settings settings) {
            super(settings);
        }

        @Override
        public int getType() {
            return TunerConstants.FILTER_SETTINGS_TLV;
        }
    }


    /**
     *  Filter Settings for a ALP filter.
     */
    public static class AlpFilterSettings extends FilterSettings {
        private int mPacketType;
        private int mLengthType;

        public AlpFilterSettings(Settings settings) {
            super(settings);
        }

        @Override
        public int getType() {
            return TunerConstants.FILTER_SETTINGS_ALP;
        }
    }


    /**
     *  Settings for filters of different subtypes.
     */
    public abstract static class Settings {
        protected final int mType;

        protected Settings(int type) {
            mType = type;
        }

        /**
         * Gets filter settings type.
         * @return
         */
        int getType() {
            return mType;
        }
    }

    /**
     *  Filter Settings for Section data according to ISO/IEC 13818-1.
     */
    public static class SectionSettings extends Settings {

        private SectionSettings(int mainType) {
            super(SectionSettings.findType(mainType));
        }

        private static int findType(int mainType) {
            switch (mainType) {
                case TunerConstants.FILTER_SETTINGS_TS:
                    return Constants.DemuxTsFilterType.SECTION;
                case TunerConstants.FILTER_SETTINGS_MMTP:
                    return Constants.DemuxMmtpFilterType.SECTION;
                case TunerConstants.FILTER_SETTINGS_IP:
                    return Constants.DemuxIpFilterType.SECTION;
                case TunerConstants.FILTER_SETTINGS_TLV:
                    return Constants.DemuxTlvFilterType.SECTION;
                case TunerConstants.FILTER_SETTINGS_ALP:
                    return Constants.DemuxAlpFilterType.SECTION;
            }
            // UNDEFINED
            return 0;
        }
    }

    /**
     *  Bits Settings for Section Filter.
     */
    public static class SectionSettingsWithSectionBits extends SectionSettings {
        private List<Byte> mFilter;
        private List<Byte> mMask;
        private List<Byte> mMode;

        private SectionSettingsWithSectionBits(int mainType) {
            super(mainType);
        }
    }

    /**
     *  Table information for Section Filter.
     */
    public static class SectionSettingsWithTableInfo extends SectionSettings {
        private int mTableId;
        private int mVersion;

        private SectionSettingsWithTableInfo(int mainType) {
            super(mainType);
        }
    }

    /**
     *  Filter Settings for a PES Data.
     */
    public static class PesSettings extends Settings {
        private int mStreamId;
        private boolean mIsRaw;

        private PesSettings(int mainType, int streamId, boolean isRaw) {
            super(PesSettings.findType(mainType));
            mStreamId = streamId;
            mIsRaw = isRaw;
        }

        private static int findType(int mainType) {
            switch (mainType) {
                case TunerConstants.FILTER_SETTINGS_TS:
                    return Constants.DemuxTsFilterType.PES;
                case TunerConstants.FILTER_SETTINGS_MMTP:
                    return Constants.DemuxMmtpFilterType.PES;
            }
            // UNDEFINED
            return 0;
        }

        /**
         * Creates a builder for PesSettings.
         */
        public static Builder newBuilder(int mainType) {
            return new Builder(mainType);
        }

        /**
         * Builder for PesSettings.
         */
        public static class Builder {
            private final int mMainType;
            private int mStreamId;
            private boolean mIsRaw;

            public Builder(int mainType) {
                mMainType = mainType;
            }

            /**
             * Sets stream ID.
             */
            public Builder setStreamId(int streamId) {
                mStreamId = streamId;
                return this;
            }

            /**
             * Sets whether it's raw.
             * true if the filter send onFilterStatus instead of onFilterEvent.
             */
            public Builder setIsRaw(boolean isRaw) {
                mIsRaw = isRaw;
                return this;
            }

            /**
             * Builds a PesSettings instance.
             */
            public PesSettings build() {
                return new PesSettings(mMainType, mStreamId, mIsRaw);
            }
        }
    }

    /**
     *  Filter Settings for a Video and Audio.
     */
    public static class AvSettings extends Settings {
        private boolean mIsPassthrough;

        private AvSettings(int mainType, boolean isAudio) {
            super(AvSettings.findType(mainType, isAudio));
        }

        private static int findType(int mainType, boolean isAudio) {
            switch (mainType) {
                case TunerConstants.FILTER_SETTINGS_TS:
                    return isAudio
                            ? Constants.DemuxTsFilterType.AUDIO
                            : Constants.DemuxTsFilterType.VIDEO;
                case TunerConstants.FILTER_SETTINGS_MMTP:
                    return isAudio
                            ? Constants.DemuxMmtpFilterType.AUDIO
                            : Constants.DemuxMmtpFilterType.VIDEO;
            }
            // UNDEFINED
            return 0;
        }
    }

    /**
     *  Filter Settings for a Download.
     */
    public static class DownloadSettings extends Settings {
        private int mDownloadId;

        public DownloadSettings(int mainType) {
            super(DownloadSettings.findType(mainType));
        }

        private static int findType(int mainType) {
            if (mainType == TunerConstants.FILTER_SETTINGS_MMTP) {
                return Constants.DemuxMmtpFilterType.DOWNLOAD;
            }
            // UNDEFINED
            return 0;
        }
    }

    /**
     *  The Settings for the record in DVR.
     */
    public static class RecordSettings extends Settings {
        private int mIndexType;
        private int mIndexMask;

        public RecordSettings(int mainType) {
            super(RecordSettings.findType(mainType));
        }

        private static int findType(int mainType) {
            switch (mainType) {
                case TunerConstants.FILTER_SETTINGS_TS:
                    return Constants.DemuxTsFilterType.RECORD;
                case TunerConstants.FILTER_SETTINGS_MMTP:
                    return Constants.DemuxMmtpFilterType.RECORD;
            }
            // UNDEFINED
            return 0;
        }
    }

}
+15 −0
Original line number Diff line number Diff line
@@ -243,9 +243,11 @@ public final class Tuner implements AutoCloseable {
        private FilterCallback mCallback;
        int mId;

        private native int nativeConfigureFilter(int type, int subType, FilterSettings settings);
        private native boolean nativeStartFilter();
        private native boolean nativeStopFilter();
        private native boolean nativeFlushFilter();
        private native int nativeRead(byte[] buffer, int offset, int size);

        private Filter(int id) {
            mId = id;
@@ -258,6 +260,14 @@ public final class Tuner implements AutoCloseable {
            }
        }

        public int configure(FilterSettings settings) {
            int subType = -1;
            if (settings.mSettings != null) {
                subType = settings.mSettings.getType();
            }
            return nativeConfigureFilter(settings.getType(), subType, settings);
        }

        public boolean start() {
            return nativeStartFilter();
        }
@@ -269,6 +279,11 @@ public final class Tuner implements AutoCloseable {
        public boolean flush() {
            return nativeFlushFilter();
        }

        public int read(@NonNull byte[] buffer, int offset, int size) {
            size = Math.min(size, buffer.length - offset);
            return nativeRead(buffer, offset, size);
        }
    }

    private Filter openFilter(int type, int subType, int bufferSize, FilterCallback cb) {
+11 −0
Original line number Diff line number Diff line
@@ -90,6 +90,17 @@ final class TunerConstants {
    public static final int FRONTEND_SETTINGS_ISDBS3 = 8;
    public static final int FRONTEND_SETTINGS_ISDBT = 9;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV,
            FILTER_SETTINGS_ALP})
    public @interface FilterSettingsType {}

    public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS;
    public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP;
    public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP;
    public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
    public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;

    private TunerConstants() {
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -135,6 +135,8 @@ cc_library_shared {
    shared_libs: [
        "android.hardware.tv.tuner@1.0",
        "libandroid_runtime",
        "libcutils",
        "libfmq",
        "libhidlbase",
        "liblog",
        "libutils",
+145 −14
Original line number Diff line number Diff line
@@ -28,8 +28,12 @@
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid;
using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
@@ -112,6 +116,26 @@ void FilterCallback::setFilter(const jobject filter) {
    mFilter = env->NewWeakGlobalRef(filter);
}

/////////////// Filter ///////////////////////

Filter::Filter(sp<IFilter> sp, jweak obj) : mFilterSp(sp), mFilterObj(obj) {}

Filter::~Filter() {
    EventFlag::deleteEventFlag(&mFilterMQEventFlag);
}

int Filter::close() {
    Result r = mFilterSp->close();
    if (r == Result::SUCCESS) {
        EventFlag::deleteEventFlag(&mFilterMQEventFlag);
    }
    return (int)r;
}

sp<IFilter> Filter::getIFilter() {
    return mFilterSp;
}

/////////////// FrontendCallback ///////////////////////

FrontendCallback::FrontendCallback(jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {}
@@ -211,6 +235,7 @@ jobject JTuner::openFrontendById(int id) {
    fe->setCallback(feCb);

    jint jId = (jint) id;

    JNIEnv *env = AndroidRuntime::getJNIEnv();
    // TODO: add more fields to frontend
    return env->NewObject(
@@ -327,18 +352,18 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
        }
    }

    sp<IFilter> filterSp;
    sp<IFilter> iFilterSp;
    sp<FilterCallback> callback = new FilterCallback();
    mDemux->openFilter(type, bufferSize, callback,
            [&](Result, const sp<IFilter>& filter) {
                filterSp = filter;
                iFilterSp = filter;
            });
    if (filterSp == NULL) {
    if (iFilterSp == NULL) {
        ALOGD("Failed to open filter, type = %d", type.mainType);
        return NULL;
    }
    int fId;
    filterSp->getId([&](Result, uint32_t filterId) {
    iFilterSp->getId([&](Result, uint32_t filterId) {
        fId = filterId;
    });

@@ -350,6 +375,7 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
                    mObject,
                    (jint) fId);

    sp<Filter> filterSp = new Filter(iFilterSp, filterObj);
    filterSp->incStrong(filterObj);
    env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get());

@@ -432,7 +458,7 @@ static DemuxPid getDemuxPid(int pidType, int pid) {
static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
    FrontendSettings frontendSettings;
    jclass clazz = env->FindClass("android/media/tv/tuner/FrontendSettings");
    jfieldID freqField = env->GetFieldID(clazz, "frequency", "I");
    jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "I");
    uint32_t freq = static_cast<uint32_t>(env->GetIntField(clazz, freqField));

    // TODO: handle the other 8 types of settings
@@ -455,8 +481,8 @@ static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject setti
    return frontendSettings;
}

static sp<IFilter> getFilter(JNIEnv *env, jobject filter) {
    return (IFilter *)env->GetLongField(filter, gFields.filterContext);
static sp<Filter> getFilter(JNIEnv *env, jobject filter) {
    return (Filter *)env->GetLongField(filter, gFields.filterContext);
}

static sp<IDvr> getDvr(JNIEnv *env, jobject dvr) {
@@ -542,8 +568,100 @@ static jobject android_media_tv_Tuner_open_filter(
    return tuner->openFilter(filterType, bufferSize);
}

static DemuxFilterSettings getFilterSettings(
        JNIEnv *env, int type, int subtype, jobject filterSettingsObj) {
    DemuxFilterSettings filterSettings;
    // TODO: more setting types
    jobject settingsObj =
            env->GetObjectField(
                    filterSettingsObj,
                    env->GetFieldID(
                            env->FindClass("android/media/tv/tuner/FilterSettings"),
                            "mSettings",
                            "Landroid/media/tv/tuner/FilterSettings$Settings;"));
    if (type == (int)DemuxFilterMainType::TS) {
        // DemuxTsFilterSettings
        jclass clazz = env->FindClass("android/media/tv/tuner/FilterSettings$TsFilterSettings");
        int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I"));
        if (subtype == (int)DemuxTsFilterType::PES) {
            // DemuxFilterPesDataSettings
            jclass settingClazz =
                    env->FindClass("android/media/tv/tuner/FilterSettings$PesSettings");
            int streamId = env->GetIntField(
                    settingsObj, env->GetFieldID(settingClazz, "mStreamId", "I"));
            bool isRaw = (bool)env->GetBooleanField(
                    settingsObj, env->GetFieldID(settingClazz, "mIsRaw", "Z"));
            DemuxFilterPesDataSettings filterPesDataSettings {
                    .streamId = static_cast<uint16_t>(streamId),
                    .isRaw = isRaw,
            };
            DemuxTsFilterSettings tsFilterSettings {
                    .tpid = static_cast<uint16_t>(tpid),
            };
            tsFilterSettings.filterSettings.pesData(filterPesDataSettings);
            filterSettings.ts(tsFilterSettings);
        }
    }
    return filterSettings;
}

static int copyData(JNIEnv *env, sp<Filter> filter, jbyteArray buffer, jint offset, int size) {
    ALOGD("copyData, size=%d, offset=%d", size, offset);

    int available = filter->mFilterMQ->availableToRead();
    ALOGD("copyData, available=%d", available);
    size = std::min(size, available);

    jboolean isCopy;
    jbyte *dst = env->GetByteArrayElements(buffer, &isCopy);
    ALOGD("copyData, isCopy=%d", isCopy);
    if (dst == nullptr) {
        ALOGD("Failed to GetByteArrayElements");
        return 0;
    }

    if (filter->mFilterMQ->read(reinterpret_cast<unsigned char*>(dst) + offset, size)) {
        env->ReleaseByteArrayElements(buffer, dst, 0);
        filter->mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
    } else {
        ALOGD("Failed to read FMQ");
        env->ReleaseByteArrayElements(buffer, dst, 0);
        return 0;
    }
    return size;
}

static int android_media_tv_Tuner_configure_filter(
        JNIEnv *env, jobject filter, int type, int subtype, jobject settings) {
    ALOGD("configure filter type=%d, subtype=%d", type, subtype);
    sp<Filter> filterSp = getFilter(env, filter);
    sp<IFilter> iFilterSp = filterSp->getIFilter();
    if (iFilterSp == NULL) {
        ALOGD("Failed to configure filter: filter not found");
        return (int)Result::INVALID_STATE;
    }
    DemuxFilterSettings filterSettings = getFilterSettings(env, type, subtype, settings);
    Result res = iFilterSp->configure(filterSettings);
    MQDescriptorSync<uint8_t> filterMQDesc;
    if (res == Result::SUCCESS && filterSp->mFilterMQ == NULL) {
        Result getQueueDescResult = Result::UNKNOWN_ERROR;
        iFilterSp->getQueueDesc(
                [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
                    filterMQDesc = desc;
                    getQueueDescResult = r;
                    ALOGD("getFilterQueueDesc");
                });
        if (getQueueDescResult == Result::SUCCESS) {
            filterSp->mFilterMQ = std::make_unique<FilterMQ>(filterMQDesc, true);
            EventFlag::createEventFlag(
                    filterSp->mFilterMQ->getEventFlagWord(), &(filterSp->mFilterMQEventFlag));
        }
    }
    return (int)res;
}

static bool android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) {
    sp<IFilter> filterSp = getFilter(env, filter);
    sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
    if (filterSp == NULL) {
        ALOGD("Failed to start filter: filter not found");
        return false;
@@ -552,7 +670,7 @@ static bool android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) {
}

static bool android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) {
    sp<IFilter> filterSp = getFilter(env, filter);
    sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
    if (filterSp == NULL) {
        ALOGD("Failed to stop filter: filter not found");
        return false;
@@ -561,7 +679,7 @@ static bool android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) {
}

static bool android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) {
    sp<IFilter> filterSp = getFilter(env, filter);
    sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
    if (filterSp == NULL) {
        ALOGD("Failed to flush filter: filter not found");
        return false;
@@ -569,6 +687,16 @@ static bool android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) {
    return filterSp->flush() == Result::SUCCESS;
}

static int android_media_tv_Tuner_read_filter_fmq(
        JNIEnv *env, jobject filter, jbyteArray buffer, jint offset, jint size) {
    sp<Filter> filterSp = getFilter(env, filter);
    if (filterSp == NULL) {
        ALOGD("Failed to read filter FMQ: filter not found");
        return 0;
    }
    return copyData(env, filterSp, buffer, offset, size);
}

static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) {
    sp<JTuner> tuner = getTuner(env, thiz);
    return tuner->openDescrambler();
@@ -580,7 +708,7 @@ static bool android_media_tv_Tuner_add_pid(
    if (descramblerSp == NULL) {
        return false;
    }
    sp<IFilter> filterSp = getFilter(env, filter);
    sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
    Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), filterSp);
    return result == Result::SUCCESS;
}
@@ -591,7 +719,7 @@ static bool android_media_tv_Tuner_remove_pid(
    if (descramblerSp == NULL) {
        return false;
    }
    sp<IFilter> filterSp = getFilter(env, filter);
    sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
    Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), filterSp);
    return result == Result::SUCCESS;
}
@@ -603,7 +731,7 @@ static jobject android_media_tv_Tuner_open_dvr(JNIEnv *env, jobject thiz, jint t

static bool android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
    sp<IDvr> dvrSp = getDvr(env, dvr);
    sp<IFilter> filterSp = getFilter(env, filter);
    sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
    if (dvrSp == NULL || filterSp == NULL) {
        return false;
    }
@@ -613,7 +741,7 @@ static bool android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobje

static bool android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) {
    sp<IDvr> dvrSp = getDvr(env, dvr);
    sp<IFilter> filterSp = getFilter(env, filter);
    sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
    if (dvrSp == NULL || filterSp == NULL) {
        return false;
    }
@@ -670,9 +798,12 @@ static const JNINativeMethod gTunerMethods[] = {
};

static const JNINativeMethod gFilterMethods[] = {
    { "nativeConfigureFilter", "(IILandroid/media/tv/tuner/FilterSettings;)I",
            (void *)android_media_tv_Tuner_configure_filter },
    { "nativeStartFilter", "()Z", (void *)android_media_tv_Tuner_start_filter },
    { "nativeStopFilter", "()Z", (void *)android_media_tv_Tuner_stop_filter },
    { "nativeFlushFilter", "()Z", (void *)android_media_tv_Tuner_flush_filter },
    { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_filter_fmq },
};

static const JNINativeMethod gDescramblerMethods[] = {
Loading