/* * 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. */ #define LOG_TAG "TvTuner-JNI" #include #include "android_media_MediaCodecLinearBlock.h" #include "android_media_tv_Tuner.h" #include "android_runtime/AndroidRuntime.h" #include #include #include #include #include #include #pragma GCC diagnostic ignored "-Wunused-function" using ::android::hardware::Void; using ::android::hardware::hidl_bitfield; using ::android::hardware::hidl_vec; using ::android::hardware::tv::tuner::V1_0::AudioExtraMetaData; using ::android::hardware::tv::tuner::V1_0::Constant; using ::android::hardware::tv::tuner::V1_0::DataFormat; using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType; using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities; using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterIpPayloadEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMediaEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMmtpRecordEvent; 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::DemuxFilterSectionBits; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterTemiEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterTsRecordEvent; using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress; using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid; using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits; using ::android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType; using ::android::hardware::tv::tuner::V1_0::DemuxScHevcIndex; using ::android::hardware::tv::tuner::V1_0::DemuxScIndex; using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterType; 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::DemuxTsIndex; using ::android::hardware::tv::tuner::V1_0::DvrSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3CodeRate; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3DemodOutputFormat; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Fec; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3PlpSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Settings; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode; using ::android::hardware::tv::tuner::V1_0::FrontendAtscSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation; using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex; using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation; using ::android::hardware::tv::tuner::V1_0::FrontendDvbcOuterFec; using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSettings; using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsCodeRate; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsPilot; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsSettings; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsVcmMode; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtPlpMode; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtSettings; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode; using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Coderate; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Settings; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsCoderate; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsSettings; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsStreamIdType; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtCoderate; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtSettings; using ::android::hardware::tv::tuner::V1_0::FrontendModulationStatus; using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo; using ::android::hardware::tv::tuner::V1_0::FrontendStatus; using ::android::hardware::tv::tuner::V1_0::FrontendStatusAtsc3PlpInfo; using ::android::hardware::tv::tuner::V1_0::FrontendStatusType; using ::android::hardware::tv::tuner::V1_0::FrontendType; using ::android::hardware::tv::tuner::V1_0::ITuner; using ::android::hardware::tv::tuner::V1_0::LnbPosition; using ::android::hardware::tv::tuner::V1_0::LnbTone; using ::android::hardware::tv::tuner::V1_0::LnbVoltage; using ::android::hardware::tv::tuner::V1_0::PlaybackSettings; using ::android::hardware::tv::tuner::V1_0::RecordSettings; struct fields_t { jfieldID tunerContext; jfieldID lnbContext; jfieldID filterContext; jfieldID timeFilterContext; jfieldID descramblerContext; jfieldID dvrRecorderContext; jfieldID dvrPlaybackContext; jfieldID mediaEventContext; jmethodID frontendInitID; jmethodID filterInitID; jmethodID timeFilterInitID; jmethodID dvrRecorderInitID; jmethodID dvrPlaybackInitID; jmethodID onFrontendEventID; jmethodID onFilterStatusID; jmethodID onFilterEventID; jmethodID lnbInitID; jmethodID onLnbEventID; jmethodID onLnbDiseqcMessageID; jmethodID onDvrRecordStatusID; jmethodID onDvrPlaybackStatusID; jmethodID descramblerInitID; jmethodID linearBlockInitID; jmethodID linearBlockSetInternalStateID; }; static fields_t gFields; static int IP_V4_LENGTH = 4; static int IP_V6_LENGTH = 16; void DestroyCallback(const C2Buffer * /* buf */, void *arg) { android::sp event = (android::MediaEvent *)arg; event->mAvHandleRefCnt--; event->finalize(); } namespace android { /////////////// LnbCallback /////////////////////// LnbCallback::LnbCallback(jobject lnbObj, LnbId id) : mId(id) { JNIEnv *env = AndroidRuntime::getJNIEnv(); mLnb = env->NewWeakGlobalRef(lnbObj); } Return LnbCallback::onEvent(LnbEventType lnbEventType) { ALOGD("LnbCallback::onEvent, type=%d", lnbEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( mLnb, gFields.onLnbEventID, (jint)lnbEventType); return Void(); } Return LnbCallback::onDiseqcMessage(const hidl_vec& diseqcMessage) { ALOGD("LnbCallback::onDiseqcMessage"); JNIEnv *env = AndroidRuntime::getJNIEnv(); jbyteArray array = env->NewByteArray(diseqcMessage.size()); env->SetByteArrayRegion( array, 0, diseqcMessage.size(), reinterpret_cast(diseqcMessage[0])); env->CallVoidMethod( mLnb, gFields.onLnbDiseqcMessageID, array); return Void(); } /////////////// Lnb /////////////////////// Lnb::Lnb(sp sp, jobject obj) : mLnbSp(sp) { JNIEnv *env = AndroidRuntime::getJNIEnv(); mLnbObj = env->NewWeakGlobalRef(obj); } Lnb::~Lnb() { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mLnbObj); mLnbObj = NULL; } sp Lnb::getILnb() { return mLnbSp; } /////////////// DvrCallback /////////////////////// Return DvrCallback::onRecordStatus(RecordStatus status) { ALOGD("DvrCallback::onRecordStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( mDvr, gFields.onDvrRecordStatusID, (jint) status); return Void(); } Return DvrCallback::onPlaybackStatus(PlaybackStatus status) { ALOGD("DvrCallback::onPlaybackStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( mDvr, gFields.onDvrPlaybackStatusID, (jint) status); return Void(); } void DvrCallback::setDvr(const jobject dvr) { ALOGD("DvrCallback::setDvr"); JNIEnv *env = AndroidRuntime::getJNIEnv(); mDvr = env->NewWeakGlobalRef(dvr); } DvrCallback::~DvrCallback() { JNIEnv *env = AndroidRuntime::getJNIEnv(); if (mDvr != NULL) { env->DeleteWeakGlobalRef(mDvr); mDvr = NULL; } } /////////////// Dvr /////////////////////// Dvr::Dvr(sp sp, jobject obj) : mDvrSp(sp), mDvrMQEventFlag(nullptr) { JNIEnv *env = AndroidRuntime::getJNIEnv(); mDvrObj = env->NewWeakGlobalRef(obj); } Dvr::~Dvr() { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mDvrObj); mDvrObj = NULL; } jint Dvr::close() { Result r = mDvrSp->close(); if (r == Result::SUCCESS) { EventFlag::deleteEventFlag(&mDvrMQEventFlag); } return (jint) r; } sp Dvr::getIDvr() { return mDvrSp; } MQ& Dvr::getDvrMQ() { return *mDvrMQ; } /////////////// C2DataIdInfo /////////////////////// C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, index) { CHECK(isGlobal()); CHECK_EQ(C2Param::INFO, kind()); DummyInfo info{value}; memcpy(this + 1, static_cast(&info) + 1, kParamSize - sizeof(C2Param)); } /////////////// MediaEvent /////////////////////// MediaEvent::MediaEvent(sp iFilter, hidl_handle avHandle, uint64_t dataId, uint64_t dataLength, jobject obj) : mIFilter(iFilter), mDataId(dataId), mDataLength(dataLength), mBuffer(nullptr), mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) { JNIEnv *env = AndroidRuntime::getJNIEnv(); mMediaEventObj = env->NewWeakGlobalRef(obj); mAvHandle = native_handle_clone(avHandle.getNativeHandle()); } MediaEvent::~MediaEvent() { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mMediaEventObj); mMediaEventObj = NULL; native_handle_delete(mAvHandle); if (mIonHandle != NULL) { delete mIonHandle; } std::shared_ptr pC2Buffer = mC2Buffer.lock(); if (pC2Buffer != NULL) { pC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this); } } void MediaEvent::finalize() { if (mAvHandleRefCnt == 0) { mIFilter->releaseAvHandle(hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0); native_handle_close(mAvHandle); } } jobject MediaEvent::getLinearBlock() { ALOGD("MediaEvent::getLinearBlock"); if (mAvHandle == NULL) { return NULL; } if (mLinearBlockObj != NULL) { return mLinearBlockObj; } mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength); std::shared_ptr block = _C2BlockFactory::CreateLinearBlock(mIonHandle); JNIEnv *env = AndroidRuntime::getJNIEnv(); std::unique_ptr context{new JMediaCodecLinearBlock}; context->mBlock = block; std::shared_ptr pC2Buffer = context->toC2Buffer(0, mDataLength); context->mBuffer = pC2Buffer; mC2Buffer = pC2Buffer; if (mAvHandle->numInts > 0) { // use first int in the native_handle as the index int index = mAvHandle->data[mAvHandle->numFds]; std::shared_ptr c2param = std::make_shared(index, mDataId); std::shared_ptr info(std::static_pointer_cast(c2param)); pC2Buffer->setInfo(info); } pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this); jobject linearBlock = env->NewObject( env->FindClass("android/media/MediaCodec$LinearBlock"), gFields.linearBlockInitID); env->CallVoidMethod( linearBlock, gFields.linearBlockSetInternalStateID, (jlong)context.release(), true); mLinearBlockObj = env->NewWeakGlobalRef(linearBlock); mAvHandleRefCnt++; return mLinearBlockObj; } uint64_t MediaEvent::getAudioHandle() { mDataIdRefCnt++; return mDataId; } /////////////// FilterCallback /////////////////////// jobjectArray FilterCallback::getSectionEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "", "(IIII)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; DemuxFilterSectionEvent sectionEvent = event.section(); jint tableId = static_cast(sectionEvent.tableId); jint version = static_cast(sectionEvent.version); jint sectionNum = static_cast(sectionEvent.sectionNum); jint dataLength = static_cast(sectionEvent.dataLength); jobject obj = env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength); env->SetObjectArrayElement(arr, i, obj); } return arr; } jobjectArray FilterCallback::getMediaEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "", "(IZJJJLandroid/media/MediaCodec$LinearBlock;" "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V"); jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; DemuxFilterMediaEvent mediaEvent = event.media(); jobject audioDescriptor = NULL; if (mediaEvent.extraMetaData.getDiscriminator() == DemuxFilterMediaEvent::ExtraMetaData::hidl_discriminator::audio) { jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor"); jmethodID adInit = env->GetMethodID(adClazz, "", "(BBCBBB)V"); AudioExtraMetaData ad = mediaEvent.extraMetaData.audio(); jbyte adFade = static_cast(ad.adFade); jbyte adPan = static_cast(ad.adPan); jchar versionTextTag = static_cast(ad.versionTextTag); jbyte adGainCenter = static_cast(ad.adGainCenter); jbyte adGainFront = static_cast(ad.adGainFront); jbyte adGainSurround = static_cast(ad.adGainSurround); audioDescriptor = env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag, adGainCenter, adGainFront, adGainSurround); } jlong dataLength = static_cast(mediaEvent.dataLength); jint streamId = static_cast(mediaEvent.streamId); jboolean isPtsPresent = static_cast(mediaEvent.isPtsPresent); jlong pts = static_cast(mediaEvent.pts); jlong offset = static_cast(mediaEvent.offset); jboolean isSecureMemory = static_cast(mediaEvent.isSecureMemory); jlong avDataId = static_cast(mediaEvent.avDataId); jint mpuSequenceNumber = static_cast(mediaEvent.mpuSequenceNumber); jboolean isPesPrivateData = static_cast(mediaEvent.isPesPrivateData); jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength, offset, NULL, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData, audioDescriptor); if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) { sp mediaEventSp = new MediaEvent(mIFilter, mediaEvent.avMemory, mediaEvent.avDataId, dataLength, obj); mediaEventSp->mAvHandleRefCnt++; env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get()); mediaEventSp->incStrong(obj); } env->SetObjectArrayElement(arr, i, obj); } return arr; } jobjectArray FilterCallback::getPesEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/PesEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "", "(III)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; DemuxFilterPesEvent pesEvent = event.pes(); jint streamId = static_cast(pesEvent.streamId); jint dataLength = static_cast(pesEvent.dataLength); jint mpuSequenceNumber = static_cast(pesEvent.mpuSequenceNumber); jobject obj = env->NewObject(eventClazz, eventInit, streamId, dataLength, mpuSequenceNumber); env->SetObjectArrayElement(arr, i, obj); } return arr; } jobjectArray FilterCallback::getTsRecordEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "", "(IIIJ)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; DemuxFilterTsRecordEvent tsRecordEvent = event.tsRecord(); DemuxPid pid = tsRecordEvent.pid; jint jpid = static_cast(Constant::INVALID_TS_PID); if (pid.getDiscriminator() == DemuxPid::hidl_discriminator::tPid) { jpid = static_cast(pid.tPid()); } else if (pid.getDiscriminator() == DemuxPid::hidl_discriminator::mmtpPid) { jpid = static_cast(pid.mmtpPid()); } jint sc = 0; if (tsRecordEvent.scIndexMask.getDiscriminator() == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::sc) { sc = static_cast(tsRecordEvent.scIndexMask.sc()); } else if (tsRecordEvent.scIndexMask.getDiscriminator() == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::scHevc) { sc = static_cast(tsRecordEvent.scIndexMask.scHevc()); } jint ts = static_cast(tsRecordEvent.tsIndexMask); jlong byteNumber = static_cast(tsRecordEvent.byteNumber); jobject obj = env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber); env->SetObjectArrayElement(arr, i, obj); } return arr; } jobjectArray FilterCallback::getMmtpRecordEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "", "(IJ)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; DemuxFilterMmtpRecordEvent mmtpRecordEvent = event.mmtpRecord(); jint scHevcIndexMask = static_cast(mmtpRecordEvent.scHevcIndexMask); jlong byteNumber = static_cast(mmtpRecordEvent.byteNumber); jobject obj = env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber); env->SetObjectArrayElement(arr, i, obj); } return arr; } jobjectArray FilterCallback::getDownloadEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "", "(IIIII)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; DemuxFilterDownloadEvent downloadEvent = event.download(); jint itemId = static_cast(downloadEvent.itemId); jint mpuSequenceNumber = static_cast(downloadEvent.mpuSequenceNumber); jint itemFragmentIndex = static_cast(downloadEvent.itemFragmentIndex); jint lastItemFragmentIndex = static_cast(downloadEvent.lastItemFragmentIndex); jint dataLength = static_cast(downloadEvent.dataLength); jobject obj = env->NewObject(eventClazz, eventInit, itemId, mpuSequenceNumber, itemFragmentIndex, lastItemFragmentIndex, dataLength); env->SetObjectArrayElement(arr, i, obj); } return arr; } jobjectArray FilterCallback::getIpPayloadEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "", "(I)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; DemuxFilterIpPayloadEvent ipPayloadEvent = event.ipPayload(); jint dataLength = static_cast(ipPayloadEvent.dataLength); jobject obj = env->NewObject(eventClazz, eventInit, dataLength); env->SetObjectArrayElement(arr, i, obj); } return arr; } jobjectArray FilterCallback::getTemiEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TemiEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "", "(JB[B)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; DemuxFilterTemiEvent temiEvent = event.temi(); jlong pts = static_cast(temiEvent.pts); jbyte descrTag = static_cast(temiEvent.descrTag); std::vector descrData = temiEvent.descrData; jbyteArray array = env->NewByteArray(descrData.size()); env->SetByteArrayRegion( array, 0, descrData.size(), reinterpret_cast(&descrData[0])); jobject obj = env->NewObject(eventClazz, eventInit, pts, descrTag, array); env->SetObjectArrayElement(arr, i, obj); } return arr; } Return FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) { ALOGD("FilterCallback::onFilterEvent"); JNIEnv *env = AndroidRuntime::getJNIEnv(); std::vector events = filterEvent.events; jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent"); jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL); if (!events.empty()) { auto event = events[0]; switch (event.getDiscriminator()) { case DemuxFilterEvent::Event::hidl_discriminator::media: { array = getMediaEvent(array, events); break; } case DemuxFilterEvent::Event::hidl_discriminator::section: { array = getSectionEvent(array, events); break; } case DemuxFilterEvent::Event::hidl_discriminator::pes: { array = getPesEvent(array, events); break; } case DemuxFilterEvent::Event::hidl_discriminator::tsRecord: { array = getTsRecordEvent(array, events); break; } case DemuxFilterEvent::Event::hidl_discriminator::mmtpRecord: { array = getMmtpRecordEvent(array, events); break; } case DemuxFilterEvent::Event::hidl_discriminator::download: { array = getDownloadEvent(array, events); break; } case DemuxFilterEvent::Event::hidl_discriminator::ipPayload: { array = getIpPayloadEvent(array, events); break; } case DemuxFilterEvent::Event::hidl_discriminator::temi: { array = getTemiEvent(array, events); break; } default: { break; } } } env->CallVoidMethod( mFilter, gFields.onFilterEventID, array); return Void(); } Return FilterCallback::onFilterStatus(const DemuxFilterStatus status) { ALOGD("FilterCallback::onFilterStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( mFilter, gFields.onFilterStatusID, (jint)status); return Void(); } void FilterCallback::setFilter(const sp filter) { ALOGD("FilterCallback::setFilter"); mFilter = filter->mFilterObj; mIFilter = filter->mFilterSp; } FilterCallback::~FilterCallback() { JNIEnv *env = AndroidRuntime::getJNIEnv(); if (mFilter != NULL) { env->DeleteWeakGlobalRef(mFilter); mFilter = NULL; } } /////////////// Filter /////////////////////// Filter::Filter(sp sp, jobject obj) : mFilterSp(sp) { JNIEnv *env = AndroidRuntime::getJNIEnv(); mFilterObj = env->NewWeakGlobalRef(obj); } Filter::~Filter() { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mFilterObj); mFilterObj = NULL; EventFlag::deleteEventFlag(&mFilterMQEventFlag); } int Filter::close() { Result r = mFilterSp->close(); if (r == Result::SUCCESS) { EventFlag::deleteEventFlag(&mFilterMQEventFlag); } return (int)r; } sp Filter::getIFilter() { return mFilterSp; } /////////////// TimeFilter /////////////////////// TimeFilter::TimeFilter(sp sp, jobject obj) : mTimeFilterSp(sp) { JNIEnv *env = AndroidRuntime::getJNIEnv(); mTimeFilterObj = env->NewWeakGlobalRef(obj); } TimeFilter::~TimeFilter() { ALOGD("~TimeFilter"); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mTimeFilterObj); mTimeFilterObj = NULL; } sp TimeFilter::getITimeFilter() { return mTimeFilterSp; } /////////////// FrontendCallback /////////////////////// FrontendCallback::FrontendCallback(jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {} Return FrontendCallback::onEvent(FrontendEventType frontendEventType) { ALOGD("FrontendCallback::onEvent, type=%d", frontendEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( mObject, gFields.onFrontendEventID, (jint)frontendEventType); return Void(); } Return FrontendCallback::onScanMessage(FrontendScanMessageType type, const FrontendScanMessage& message) { ALOGD("FrontendCallback::onScanMessage, type=%d", type); JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); switch(type) { case FrontendScanMessageType::LOCKED: { if (message.isLocked()) { env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onLocked", "()V")); } break; } case FrontendScanMessageType::END: { if (message.isEnd()) { env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onScanStopped", "()V")); } break; } case FrontendScanMessageType::PROGRESS_PERCENT: { env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onProgress", "(I)V"), (jint) message.progressPercent()); break; } case FrontendScanMessageType::FREQUENCY: { std::vector v = message.frequencies(); jintArray freqs = env->NewIntArray(v.size()); env->SetIntArrayRegion(freqs, 0, v.size(), reinterpret_cast(&v[0])); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onFrequenciesReport", "([I)V"), freqs); break; } case FrontendScanMessageType::SYMBOL_RATE: { std::vector v = message.symbolRates(); jintArray symbolRates = env->NewIntArray(v.size()); env->SetIntArrayRegion(symbolRates, 0, v.size(), reinterpret_cast(&v[0])); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onSymbolRates", "([I)V"), symbolRates); break; } case FrontendScanMessageType::HIERARCHY: { env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onHierarchy", "(I)V"), (jint) message.hierarchy()); break; } case FrontendScanMessageType::ANALOG_TYPE: { env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onSignalType", "(I)V"), (jint) message.analogType()); break; } case FrontendScanMessageType::PLP_IDS: { std::vector v = message.plpIds(); std::vector jintV(v.begin(), v.end()); jintArray plpIds = env->NewIntArray(v.size()); env->SetIntArrayRegion(plpIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onPlpIds", "([I)V"), plpIds); break; } case FrontendScanMessageType::GROUP_IDS: { std::vector v = message.groupIds(); std::vector jintV(v.begin(), v.end()); jintArray groupIds = env->NewIntArray(v.size()); env->SetIntArrayRegion(groupIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onGroupIds", "([I)V"), groupIds); break; } case FrontendScanMessageType::INPUT_STREAM_IDS: { std::vector v = message.inputStreamIds(); std::vector jintV(v.begin(), v.end()); jintArray streamIds = env->NewIntArray(v.size()); env->SetIntArrayRegion(streamIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onInputStreamIds", "([I)V"), streamIds); break; } case FrontendScanMessageType::STANDARD: { FrontendScanMessage::Standard std = message.std(); jint standard; if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sStd) { standard = (jint) std.sStd(); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onDvbsStandard", "(I)V"), standard); } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::tStd) { standard = (jint) std.tStd(); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onDvbtStandard", "(I)V"), standard); } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sifStd) { standard = (jint) std.sifStd(); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onAnalogSifStandard", "(I)V"), standard); } break; } case FrontendScanMessageType::ATSC3_PLP_INFO: { jclass plpClazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo"); jmethodID init = env->GetMethodID(plpClazz, "", "(IZ)V"); std::vector plpInfos = message.atsc3PlpInfos(); jobjectArray array = env->NewObjectArray(plpInfos.size(), plpClazz, NULL); for (int i = 0; i < plpInfos.size(); i++) { auto info = plpInfos[i]; jint plpId = (jint) info.plpId; jboolean lls = (jboolean) info.bLlsFlag; jobject obj = env->NewObject(plpClazz, init, plpId, lls); env->SetObjectArrayElement(array, i, obj); } env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onAtsc3PlpInfos", "([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"), array); break; } } return Void(); } /////////////// Tuner /////////////////////// sp JTuner::mTuner; JTuner::JTuner(JNIEnv *env, jobject thiz) : mClass(NULL) { jclass clazz = env->GetObjectClass(thiz); CHECK(clazz != NULL); mClass = (jclass)env->NewGlobalRef(clazz); mObject = env->NewWeakGlobalRef(thiz); if (mTuner == NULL) { mTuner = getTunerService(); } } JTuner::~JTuner() { if (mFe != NULL) { mFe->close(); } if (mDemux != NULL) { mDemux->close(); } JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mObject); env->DeleteGlobalRef(mClass); mTuner = NULL; mClass = NULL; mObject = NULL; } sp JTuner::getTunerService() { if (mTuner == nullptr) { mTuner = ITuner::getService(); if (mTuner == nullptr) { ALOGW("Failed to get tuner service."); } } return mTuner; } jobject JTuner::getFrontendIds() { ALOGD("JTuner::getFrontendIds()"); mTuner->getFrontendIds([&](Result, const hidl_vec& frontendIds) { mFeIds = frontendIds; }); if (mFeIds.size() == 0) { ALOGW("Frontend isn't available"); return NULL; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass arrayListClazz = env->FindClass("java/util/ArrayList"); jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z"); jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "", "()V")); jclass integerClazz = env->FindClass("java/lang/Integer"); jmethodID intInit = env->GetMethodID(integerClazz, "", "(I)V"); for (int i=0; i < mFeIds.size(); i++) { jobject idObj = env->NewObject(integerClazz, intInit, mFeIds[i]); env->CallBooleanMethod(obj, arrayListAdd, idObj); } return obj; } jobject JTuner::openFrontendById(int id) { sp fe; Result res; mTuner->openFrontendById(id, [&](Result r, const sp& frontend) { fe = frontend; res = r; }); if (res != Result::SUCCESS || fe == nullptr) { ALOGE("Failed to open frontend"); return NULL; } mFe = fe; mFeId = id; if (mDemux != NULL) { mDemux->setFrontendDataSource(mFeId); } sp feCb = new FrontendCallback(mObject, id); fe->setCallback(feCb); jint jId = (jint) id; JNIEnv *env = AndroidRuntime::getJNIEnv(); // TODO: add more fields to frontend return env->NewObject( env->FindClass("android/media/tv/tuner/Tuner$Frontend"), gFields.frontendInitID, mObject, (jint) jId); } jint JTuner::closeFrontendById(int id) { if (mFe != NULL && mFeId == id) { Result r = mFe->close(); return (jint) r; } return (jint) Result::SUCCESS; } jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(II)V"); jint typeCap = caps.analogCaps().typeCap; jint sifStandardCap = caps.analogCaps().sifStandardCap; return env->NewObject(clazz, capsInit, typeCap, sifStandardCap); } jobject JTuner::getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIIII)V"); jint bandwidthCap = caps.atsc3Caps().bandwidthCap; jint modulationCap = caps.atsc3Caps().modulationCap; jint timeInterleaveModeCap = caps.atsc3Caps().timeInterleaveModeCap; jint codeRateCap = caps.atsc3Caps().codeRateCap; jint fecCap = caps.atsc3Caps().fecCap; jint demodOutputFormatCap = caps.atsc3Caps().demodOutputFormatCap; return env->NewObject(clazz, capsInit, bandwidthCap, modulationCap, timeInterleaveModeCap, codeRateCap, fecCap, demodOutputFormatCap); } jobject JTuner::getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AtscFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(I)V"); jint modulationCap = caps.atscCaps().modulationCap; return env->NewObject(clazz, capsInit, modulationCap); } jobject JTuner::getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(III)V"); jint modulationCap = caps.dvbcCaps().modulationCap; jint fecCap = caps.dvbcCaps().fecCap; jint annexCap = caps.dvbcCaps().annexCap; return env->NewObject(clazz, capsInit, modulationCap, fecCap, annexCap); } jobject JTuner::getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IJI)V"); jint modulationCap = caps.dvbsCaps().modulationCap; jlong innerfecCap = caps.dvbsCaps().innerfecCap; jint standard = caps.dvbsCaps().standard; return env->NewObject(clazz, capsInit, modulationCap, innerfecCap, standard); } jobject JTuner::getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIIIIZZ)V"); jint transmissionModeCap = caps.dvbtCaps().transmissionModeCap; jint bandwidthCap = caps.dvbtCaps().bandwidthCap; jint constellationCap = caps.dvbtCaps().constellationCap; jint coderateCap = caps.dvbtCaps().coderateCap; jint hierarchyCap = caps.dvbtCaps().hierarchyCap; jint guardIntervalCap = caps.dvbtCaps().guardIntervalCap; jboolean isT2Supported = caps.dvbtCaps().isT2Supported; jboolean isMisoSupported = caps.dvbtCaps().isMisoSupported; return env->NewObject(clazz, capsInit, transmissionModeCap, bandwidthCap, constellationCap, coderateCap, hierarchyCap, guardIntervalCap, isT2Supported, isMisoSupported); } jobject JTuner::getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(II)V"); jint modulationCap = caps.isdbs3Caps().modulationCap; jint coderateCap = caps.isdbs3Caps().coderateCap; return env->NewObject(clazz, capsInit, modulationCap, coderateCap); } jobject JTuner::getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(II)V"); jint modulationCap = caps.isdbsCaps().modulationCap; jint coderateCap = caps.isdbsCaps().coderateCap; return env->NewObject(clazz, capsInit, modulationCap, coderateCap); } jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIII)V"); jint modeCap = caps.isdbtCaps().modeCap; jint bandwidthCap = caps.isdbtCaps().bandwidthCap; jint modulationCap = caps.isdbtCaps().modulationCap; jint coderateCap = caps.isdbtCaps().coderateCap; jint guardIntervalCap = caps.isdbtCaps().guardIntervalCap; return env->NewObject(clazz, capsInit, modeCap, bandwidthCap, modulationCap, coderateCap, guardIntervalCap); } jobject JTuner::getFrontendInfo(int id) { FrontendInfo feInfo; Result res; mTuner->getFrontendInfo(id, [&](Result r, const FrontendInfo& info) { feInfo = info; res = r; }); if (res != Result::SUCCESS) { return NULL; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendInfo"); jmethodID infoInit = env->GetMethodID(clazz, "", "(IIIIIIII[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V"); jint type = (jint) feInfo.type; jint minFrequency = feInfo.minFrequency; jint maxFrequency = feInfo.maxFrequency; jint minSymbolRate = feInfo.minSymbolRate; jint maxSymbolRate = feInfo.maxSymbolRate; jint acquireRange = feInfo.acquireRange; jint exclusiveGroupId = feInfo.exclusiveGroupId; jintArray statusCaps = env->NewIntArray(feInfo.statusCaps.size()); env->SetIntArrayRegion( statusCaps, 0, feInfo.statusCaps.size(), reinterpret_cast(&feInfo.statusCaps[0])); FrontendInfo::FrontendCapabilities caps = feInfo.frontendCaps; jobject jcaps = NULL; switch(feInfo.type) { case FrontendType::ANALOG: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::analogCaps == caps.getDiscriminator()) { jcaps = getAnalogFrontendCaps(env, caps); } break; case FrontendType::ATSC3: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::atsc3Caps == caps.getDiscriminator()) { jcaps = getAtsc3FrontendCaps(env, caps); } break; case FrontendType::ATSC: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::atscCaps == caps.getDiscriminator()) { jcaps = getAtscFrontendCaps(env, caps); } break; case FrontendType::DVBC: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::dvbcCaps == caps.getDiscriminator()) { jcaps = getDvbcFrontendCaps(env, caps); } break; case FrontendType::DVBS: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::dvbsCaps == caps.getDiscriminator()) { jcaps = getDvbsFrontendCaps(env, caps); } break; case FrontendType::DVBT: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::dvbtCaps == caps.getDiscriminator()) { jcaps = getDvbtFrontendCaps(env, caps); } break; case FrontendType::ISDBS: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::isdbsCaps == caps.getDiscriminator()) { jcaps = getIsdbsFrontendCaps(env, caps); } break; case FrontendType::ISDBS3: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::isdbs3Caps == caps.getDiscriminator()) { jcaps = getIsdbs3FrontendCaps(env, caps); } break; case FrontendType::ISDBT: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::isdbtCaps == caps.getDiscriminator()) { jcaps = getIsdbtFrontendCaps(env, caps); } break; default: break; } return env->NewObject( clazz, infoInit, (jint) id, type, minFrequency, maxFrequency, minSymbolRate, maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps); } jintArray JTuner::getLnbIds() { ALOGD("JTuner::getLnbIds()"); Result res; hidl_vec lnbIds; mTuner->getLnbIds([&](Result r, const hidl_vec& ids) { lnbIds = ids; res = r; }); if (res != Result::SUCCESS || lnbIds.size() == 0) { ALOGW("Lnb isn't available"); return NULL; } mLnbIds = lnbIds; JNIEnv *env = AndroidRuntime::getJNIEnv(); jintArray ids = env->NewIntArray(mLnbIds.size()); env->SetIntArrayRegion(ids, 0, mLnbIds.size(), reinterpret_cast(&mLnbIds[0])); return ids; } jobject JTuner::openLnbById(int id) { sp iLnbSp; Result r; mTuner->openLnbById(id, [&](Result res, const sp& lnb) { r = res; iLnbSp = lnb; }); if (r != Result::SUCCESS || iLnbSp == nullptr) { ALOGE("Failed to open lnb"); return NULL; } mLnb = iLnbSp; JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject lnbObj = env->NewObject( env->FindClass("android/media/tv/tuner/Lnb"), gFields.lnbInitID, (jint) id); sp lnbCb = new LnbCallback(lnbObj, id); mLnb->setCallback(lnbCb); sp lnbSp = new Lnb(iLnbSp, lnbObj); lnbSp->incStrong(lnbObj); env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get()); return lnbObj; } jobject JTuner::openLnbByName(jstring name) { JNIEnv *env = AndroidRuntime::getJNIEnv(); std::string lnbName(env->GetStringUTFChars(name, nullptr)); sp iLnbSp; Result res; LnbId id; mTuner->openLnbByName(lnbName, [&](Result r, LnbId lnbId, const sp& lnb) { res = r; iLnbSp = lnb; id = lnbId; }); if (res != Result::SUCCESS || iLnbSp == nullptr) { ALOGE("Failed to open lnb"); return NULL; } mLnb = iLnbSp; jobject lnbObj = env->NewObject( env->FindClass("android/media/tv/tuner/Lnb"), gFields.lnbInitID, id); sp lnbCb = new LnbCallback(lnbObj, id); mLnb->setCallback(lnbCb); sp lnbSp = new Lnb(iLnbSp, lnbObj); lnbSp->incStrong(lnbObj); env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get()); return lnbObj; } int JTuner::tune(const FrontendSettings& settings) { if (mFe == NULL) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFe->tune(settings); return (int)result; } int JTuner::stopTune() { if (mFe == NULL) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFe->stopTune(); return (int)result; } int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType) { if (mFe == NULL) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFe->scan(settings, scanType); return (int)result; } int JTuner::stopScan() { if (mFe == NULL) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFe->stopScan(); return (int)result; } int JTuner::setLnb(int id) { if (mFe == NULL) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFe->setLnb(id); return (int)result; } int JTuner::setLna(bool enable) { if (mFe == NULL) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFe->setLna(enable); return (int)result; } Result JTuner::openDemux() { if (mTuner == nullptr) { return Result::NOT_INITIALIZED; } if (mDemux != nullptr) { return Result::SUCCESS; } Result res; uint32_t id; sp demuxSp; mTuner->openDemux([&](Result r, uint32_t demuxId, const sp& demux) { demuxSp = demux; id = demuxId; res = r; ALOGD("open demux, id = %d", demuxId); }); if (res == Result::SUCCESS) { mDemux = demuxSp; mDemuxId = id; if (mFe != NULL) { mDemux->setFrontendDataSource(mFeId); } } return res; } jint JTuner::close() { Result res = Result::SUCCESS; if (mFe != NULL) { res = mFe->close(); if (res != Result::SUCCESS) { return (jint) res; } } if (mDemux != NULL) { res = mDemux->close(); if (res != Result::SUCCESS) { return (jint) res; } } return (jint) res; } jobject JTuner::getAvSyncHwId(sp filter) { if (mDemux == NULL) { return NULL; } uint32_t avSyncHwId; Result res; sp iFilterSp = filter->getIFilter(); mDemux->getAvSyncHwId(iFilterSp, [&](Result r, uint32_t id) { res = r; avSyncHwId = id; }); if (res == Result::SUCCESS) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass integerClazz = env->FindClass("java/lang/Integer"); jmethodID intInit = env->GetMethodID(integerClazz, "", "(I)V"); return env->NewObject(integerClazz, intInit, avSyncHwId); } return NULL; } jobject JTuner::getAvSyncTime(jint id) { if (mDemux == NULL) { return NULL; } uint64_t time; Result res; mDemux->getAvSyncTime(static_cast(id), [&](Result r, uint64_t ts) { res = r; time = ts; }); if (res == Result::SUCCESS) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); return env->NewObject(longClazz, longInit, static_cast(time)); } return NULL; } int JTuner::connectCiCam(jint id) { if (mDemux == NULL) { Result r = openDemux(); if (r != Result::SUCCESS) { return (int) r; } } Result r = mDemux->connectCiCam(static_cast(id)); return (int) r; } int JTuner::disconnectCiCam() { if (mDemux == NULL) { Result r = openDemux(); if (r != Result::SUCCESS) { return (int) r; } } Result r = mDemux->disconnectCiCam(); return (int) r; } jobject JTuner::openDescrambler() { ALOGD("JTuner::openDescrambler"); if (mTuner == nullptr || mDemux == nullptr) { return NULL; } sp descramblerSp; Result res; mTuner->openDescrambler([&](Result r, const sp& descrambler) { res = r; descramblerSp = descrambler; }); if (res != Result::SUCCESS || descramblerSp == NULL) { return NULL; } descramblerSp->setDemuxSource(mDemuxId); JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject descramblerObj = env->NewObject( env->FindClass("android/media/tv/tuner/Descrambler"), gFields.descramblerInitID); descramblerSp->incStrong(descramblerObj); env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerSp.get()); return descramblerObj; } jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { if (mDemux == NULL) { if (openDemux() != Result::SUCCESS) { return NULL; } } sp iFilterSp; sp callback = new FilterCallback(); Result res; mDemux->openFilter(type, bufferSize, callback, [&](Result r, const sp& filter) { iFilterSp = filter; res = r; }); if (res != Result::SUCCESS || iFilterSp == NULL) { ALOGD("Failed to open filter, type = %d", type.mainType); return NULL; } int fId; iFilterSp->getId([&](Result, uint32_t filterId) { fId = filterId; }); JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject filterObj = env->NewObject( env->FindClass("android/media/tv/tuner/filter/Filter"), gFields.filterInitID, (jint) fId); sp filterSp = new Filter(iFilterSp, filterObj); filterSp->incStrong(filterObj); env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get()); callback->setFilter(filterSp); return filterObj; } jobject JTuner::openTimeFilter() { if (mDemux == NULL) { if (openDemux() != Result::SUCCESS) { return NULL; } } sp iTimeFilterSp; Result res; mDemux->openTimeFilter( [&](Result r, const sp& filter) { iTimeFilterSp = filter; res = r; }); if (res != Result::SUCCESS || iTimeFilterSp == NULL) { return NULL; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject timeFilterObj = env->NewObject( env->FindClass("android/media/tv/tuner/filter/TimeFilter"), gFields.timeFilterInitID); sp timeFilterSp = new TimeFilter(iTimeFilterSp, timeFilterObj); timeFilterSp->incStrong(timeFilterObj); env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterSp.get()); return timeFilterObj; } jobject JTuner::openDvr(DvrType type, jlong bufferSize) { ALOGD("JTuner::openDvr"); if (mDemux == NULL) { if (openDemux() != Result::SUCCESS) { return NULL; } } sp iDvrSp; sp callback = new DvrCallback(); Result res; mDemux->openDvr(type, (uint32_t) bufferSize, callback, [&](Result r, const sp& dvr) { res = r; iDvrSp = dvr; }); if (res != Result::SUCCESS || iDvrSp == NULL) { return NULL; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject dvrObj; if (type == DvrType::RECORD) { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"), gFields.dvrRecorderInitID, mObject); sp dvrSp = new Dvr(iDvrSp, dvrObj); dvrSp->incStrong(dvrObj); env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrSp.get()); } else { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"), gFields.dvrPlaybackInitID, mObject); sp dvrSp = new Dvr(iDvrSp, dvrObj); dvrSp->incStrong(dvrObj); env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrSp.get()); } callback->setDvr(dvrObj); return dvrObj; } jobject JTuner::getDemuxCaps() { DemuxCapabilities caps; Result res; mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) { caps = demuxCaps; res = r; }); if (res != Result::SUCCESS) { return NULL; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIIIIIIIJI[IZ)V"); jint numDemux = caps.numDemux; jint numRecord = caps.numRecord; jint numPlayback = caps.numPlayback; jint numTsFilter = caps.numTsFilter; jint numSectionFilter = caps.numSectionFilter; jint numAudioFilter = caps.numAudioFilter; jint numVideoFilter = caps.numVideoFilter; jint numPesFilter = caps.numPesFilter; jint numPcrFilter = caps.numPcrFilter; jlong numBytesInSectionFilter = caps.numBytesInSectionFilter; jint filterCaps = static_cast(caps.filterCaps); jboolean bTimeFilter = caps.bTimeFilter; jintArray linkCaps = env->NewIntArray(caps.linkCaps.size()); env->SetIntArrayRegion( linkCaps, 0, caps.linkCaps.size(), reinterpret_cast(&caps.linkCaps[0])); return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter, numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter, numBytesInSectionFilter, filterCaps, linkCaps, bTimeFilter); } jobject JTuner::getFrontendStatus(jintArray types) { if (mFe == NULL) { return NULL; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jsize size = env->GetArrayLength(types); std::vector v(size); env->GetIntArrayRegion(types, 0, size, reinterpret_cast(&v[0])); Result res; hidl_vec status; mFe->getStatus(v, [&](Result r, const hidl_vec& s) { res = r; status = s; }); if (res != Result::SUCCESS) { return NULL; } jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatus"); jmethodID init = env->GetMethodID(clazz, "", "()V"); jobject statusObj = env->NewObject(clazz, init); jclass intClazz = env->FindClass("java/lang/Integer"); jmethodID initInt = env->GetMethodID(intClazz, "", "(I)V"); jclass booleanClazz = env->FindClass("java/lang/Boolean"); jmethodID initBoolean = env->GetMethodID(booleanClazz, "", "(Z)V"); for (auto s : status) { switch(s.getDiscriminator()) { case FrontendStatus::hidl_discriminator::isDemodLocked: { jfieldID field = env->GetFieldID(clazz, "mIsDemodLocked", "Ljava/lang/Boolean;"); jobject newBooleanObj = env->NewObject( booleanClazz, initBoolean, static_cast(s.isDemodLocked())); env->SetObjectField(statusObj, field, newBooleanObj); break; } case FrontendStatus::hidl_discriminator::snr: { jfieldID field = env->GetFieldID(clazz, "mSnr", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.snr())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::ber: { jfieldID field = env->GetFieldID(clazz, "mBer", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.ber())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::per: { jfieldID field = env->GetFieldID(clazz, "mPer", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.per())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::preBer: { jfieldID field = env->GetFieldID(clazz, "mPerBer", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.preBer())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::signalQuality: { jfieldID field = env->GetFieldID(clazz, "mSignalQuality", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.signalQuality())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::signalStrength: { jfieldID field = env->GetFieldID(clazz, "mSignalStrength", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.signalStrength())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::symbolRate: { jfieldID field = env->GetFieldID(clazz, "mSymbolRate", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.symbolRate())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::innerFec: { jfieldID field = env->GetFieldID(clazz, "mInnerFec", "Ljava/lang/Long;"); jclass longClazz = env->FindClass("java/lang/Long"); jmethodID initLong = env->GetMethodID(longClazz, "", "(J)V"); jobject newLongObj = env->NewObject( longClazz, initLong, static_cast(s.innerFec())); env->SetObjectField(statusObj, field, newLongObj); break; } case FrontendStatus::hidl_discriminator::modulation: { jfieldID field = env->GetFieldID(clazz, "mModulation", "Ljava/lang/Integer;"); FrontendModulationStatus modulation = s.modulation(); jint intModulation; bool valid = true; switch(modulation.getDiscriminator()) { case FrontendModulationStatus::hidl_discriminator::dvbc: { intModulation = static_cast(modulation.dvbc()); break; } case FrontendModulationStatus::hidl_discriminator::dvbs: { intModulation = static_cast(modulation.dvbs()); break; } case FrontendModulationStatus::hidl_discriminator::isdbs: { intModulation = static_cast(modulation.isdbs()); break; } case FrontendModulationStatus::hidl_discriminator::isdbs3: { intModulation = static_cast(modulation.isdbs3()); break; } case FrontendModulationStatus::hidl_discriminator::isdbt: { intModulation = static_cast(modulation.isdbt()); break; } default: { valid = false; break; } } if (valid) { jobject newIntegerObj = env->NewObject(intClazz, initInt, intModulation); env->SetObjectField(statusObj, field, newIntegerObj); } break; } case FrontendStatus::hidl_discriminator::inversion: { jfieldID field = env->GetFieldID(clazz, "mInversion", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.inversion())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::lnbVoltage: { jfieldID field = env->GetFieldID(clazz, "mLnbVoltage", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.lnbVoltage())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::plpId: { jfieldID field = env->GetFieldID(clazz, "mPlpId", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.plpId())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::isEWBS: { jfieldID field = env->GetFieldID(clazz, "mIsEwbs", "Ljava/lang/Boolean;"); jobject newBooleanObj = env->NewObject( booleanClazz, initBoolean, static_cast(s.isEWBS())); env->SetObjectField(statusObj, field, newBooleanObj); break; } case FrontendStatus::hidl_discriminator::agc: { jfieldID field = env->GetFieldID(clazz, "mAgc", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.agc())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::isLnaOn: { jfieldID field = env->GetFieldID(clazz, "mIsLnaOn", "Ljava/lang/Boolean;"); jobject newBooleanObj = env->NewObject( booleanClazz, initBoolean, static_cast(s.isLnaOn())); env->SetObjectField(statusObj, field, newBooleanObj); break; } case FrontendStatus::hidl_discriminator::isLayerError: { jfieldID field = env->GetFieldID(clazz, "mIsLayerErrors", "[Z"); hidl_vec layerErr = s.isLayerError(); jbooleanArray valObj = env->NewBooleanArray(layerErr.size()); for (size_t i = 0; i < layerErr.size(); i++) { jboolean x = layerErr[i]; env->SetBooleanArrayRegion(valObj, i, 1, &x); } env->SetObjectField(statusObj, field, valObj); break; } case FrontendStatus::hidl_discriminator::mer: { jfieldID field = env->GetFieldID(clazz, "mMer", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.mer())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::freqOffset: { jfieldID field = env->GetFieldID(clazz, "mFreqOffset", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.freqOffset())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::hierarchy: { jfieldID field = env->GetFieldID(clazz, "mHierarchy", "Ljava/lang/Integer;"); jobject newIntegerObj = env->NewObject( intClazz, initInt, static_cast(s.hierarchy())); env->SetObjectField(statusObj, field, newIntegerObj); break; } case FrontendStatus::hidl_discriminator::isRfLocked: { jfieldID field = env->GetFieldID(clazz, "mIsRfLocked", "Ljava/lang/Boolean;"); jobject newBooleanObj = env->NewObject( booleanClazz, initBoolean, static_cast(s.isRfLocked())); env->SetObjectField(statusObj, field, newBooleanObj); break; } case FrontendStatus::hidl_discriminator::plpInfo: { jfieldID field = env->GetFieldID(clazz, "mPlpInfo", "[Landroid/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo;"); jclass plpClazz = env->FindClass( "android/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo"); jmethodID initPlp = env->GetMethodID(plpClazz, "", "(IZI)V"); hidl_vec plpInfos = s.plpInfo(); jobjectArray valObj = env->NewObjectArray(plpInfos.size(), plpClazz, NULL); for (int i = 0; i < plpInfos.size(); i++) { auto info = plpInfos[i]; jint plpId = (jint) info.plpId; jboolean isLocked = (jboolean) info.isLocked; jint uec = (jint) info.uec; jobject plpObj = env->NewObject(plpClazz, initPlp, plpId, isLocked, uec); env->SetObjectArrayElement(valObj, i, plpObj); } env->SetObjectField(statusObj, field, valObj); break; } default: { break; } } } return statusObj; } jint JTuner::closeFrontend() { Result r = Result::SUCCESS; if (mFe != NULL) { r = mFe->close(); } return (jint) r; } jint JTuner::closeDemux() { Result r = Result::SUCCESS; if (mDemux != NULL) { r = mDemux->close(); } return (jint) r; } } // namespace android //////////////////////////////////////////////////////////////////////////////// using namespace android; static sp setTuner(JNIEnv *env, jobject thiz, const sp &tuner) { sp old = (JTuner *)env->GetLongField(thiz, gFields.tunerContext); if (tuner != NULL) { tuner->incStrong(thiz); } if (old != NULL) { old->decStrong(thiz); } env->SetLongField(thiz, gFields.tunerContext, (jlong)tuner.get()); return old; } static sp getTuner(JNIEnv *env, jobject thiz) { return (JTuner *)env->GetLongField(thiz, gFields.tunerContext); } static sp getDescrambler(JNIEnv *env, jobject descrambler) { return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext); } static uint32_t getResourceIdFromHandle(jint handle) { return (handle & 0x00ff0000) >> 16; } static DemuxPid getDemuxPid(int pidType, int pid) { DemuxPid demuxPid; if ((int)pidType == 1) { demuxPid.tPid(static_cast(pid)); } else if ((int)pidType == 2) { demuxPid.mmtpPid(static_cast(pid)); } return demuxPid; } static uint32_t getFrontendSettingsFreq(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings"); jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "I"); uint32_t freq = static_cast(env->GetIntField(settings, freqField)); return freq; } static FrontendSettings getAnalogFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendSettings"); FrontendAnalogType analogType = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mSignalType", "I"))); FrontendAnalogSifStandard sifStandard = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mSifStandard", "I"))); FrontendAnalogSettings frontendAnalogSettings { .frequency = freq, .type = analogType, .sifStandard = sifStandard, }; frontendSettings.analog(frontendAnalogSettings); return frontendSettings; } static hidl_vec getAtsc3PlpSettings( JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendSettings"); jobjectArray plpSettings = reinterpret_cast( env->GetObjectField(settings, env->GetFieldID( clazz, "mPlpSettings", "[Landroid/media/tv/tuner/frontend/Atsc3PlpSettings;"))); int len = env->GetArrayLength(plpSettings); jclass plpClazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpSettings"); hidl_vec plps = hidl_vec(len); // parse PLP settings for (int i = 0; i < len; i++) { jobject plp = env->GetObjectArrayElement(plpSettings, i); uint8_t plpId = static_cast( env->GetIntField(plp, env->GetFieldID(plpClazz, "mPlpId", "I"))); FrontendAtsc3Modulation modulation = static_cast( env->GetIntField(plp, env->GetFieldID(plpClazz, "mModulation", "I"))); FrontendAtsc3TimeInterleaveMode interleaveMode = static_cast( env->GetIntField( plp, env->GetFieldID(plpClazz, "mInterleaveMode", "I"))); FrontendAtsc3CodeRate codeRate = static_cast( env->GetIntField(plp, env->GetFieldID(plpClazz, "mCodeRate", "I"))); FrontendAtsc3Fec fec = static_cast( env->GetIntField(plp, env->GetFieldID(plpClazz, "mFec", "I"))); FrontendAtsc3PlpSettings frontendAtsc3PlpSettings { .plpId = plpId, .modulation = modulation, .interleaveMode = interleaveMode, .codeRate = codeRate, .fec = fec, }; plps[i] = frontendAtsc3PlpSettings; } return plps; } static FrontendSettings getAtsc3FrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendSettings"); FrontendAtsc3Bandwidth bandwidth = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I"))); FrontendAtsc3DemodOutputFormat demod = static_cast( env->GetIntField( settings, env->GetFieldID(clazz, "mDemodOutputFormat", "I"))); hidl_vec plps = getAtsc3PlpSettings(env, settings); FrontendAtsc3Settings frontendAtsc3Settings { .frequency = freq, .bandwidth = bandwidth, .demodOutputFormat = demod, .plpSettings = plps, }; frontendSettings.atsc3(frontendAtsc3Settings); return frontendSettings; } static FrontendSettings getAtscFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AtscFrontendSettings"); FrontendAtscModulation modulation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); FrontendAtscSettings frontendAtscSettings { .frequency = freq, .modulation = modulation, }; frontendSettings.atsc(frontendAtscSettings); return frontendSettings; } static FrontendSettings getDvbcFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendSettings"); FrontendDvbcModulation modulation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); FrontendInnerFec innerFec = static_cast( env->GetLongField(settings, env->GetFieldID(clazz, "mFec", "J"))); uint32_t symbolRate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I"))); FrontendDvbcOuterFec outerFec = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mOuterFec", "I"))); FrontendDvbcAnnex annex = static_cast( env->GetByteField(settings, env->GetFieldID(clazz, "mAnnex", "B"))); FrontendDvbcSpectralInversion spectralInversion = static_cast( env->GetIntField( settings, env->GetFieldID(clazz, "mSpectralInversion", "I"))); FrontendDvbcSettings frontendDvbcSettings { .frequency = freq, .modulation = modulation, .fec = innerFec, .symbolRate = symbolRate, .outerFec = outerFec, .annex = annex, .spectralInversion = spectralInversion, }; frontendSettings.dvbc(frontendDvbcSettings); return frontendSettings; } static FrontendDvbsCodeRate getDvbsCodeRate(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings"); jobject jcodeRate = env->GetObjectField(settings, env->GetFieldID( clazz, "mCodeRate", "Landroid/media/tv/tuner/frontend/DvbsCodeRate;")); jclass codeRateClazz = env->FindClass("android/media/tv/tuner/frontend/DvbsCodeRate"); FrontendInnerFec innerFec = static_cast( env->GetLongField( jcodeRate, env->GetFieldID(codeRateClazz, "mInnerFec", "J"))); bool isLinear = static_cast( env->GetBooleanField( jcodeRate, env->GetFieldID(codeRateClazz, "mIsLinear", "Z"))); bool isShortFrames = static_cast( env->GetBooleanField( jcodeRate, env->GetFieldID(codeRateClazz, "mIsShortFrames", "Z"))); uint32_t bitsPer1000Symbol = static_cast( env->GetIntField( jcodeRate, env->GetFieldID( codeRateClazz, "mBitsPer1000Symbol", "I"))); FrontendDvbsCodeRate coderate { .fec = innerFec, .isLinear = isLinear, .isShortFrames = isShortFrames, .bitsPer1000Symbol = bitsPer1000Symbol, }; return coderate; } static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings"); FrontendDvbsModulation modulation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); uint32_t symbolRate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I"))); FrontendDvbsRolloff rolloff = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mRolloff", "I"))); FrontendDvbsPilot pilot = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mPilot", "I"))); uint32_t inputStreamId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mInputStreamId", "I"))); FrontendDvbsStandard standard = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mStandard", "I"))); FrontendDvbsVcmMode vcmMode = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mVcmMode", "I"))); FrontendDvbsCodeRate coderate = getDvbsCodeRate(env, settings); FrontendDvbsSettings frontendDvbsSettings { .frequency = freq, .modulation = modulation, .coderate = coderate, .symbolRate = symbolRate, .rolloff = rolloff, .pilot = pilot, .inputStreamId = inputStreamId, .standard = standard, .vcmMode = vcmMode, }; frontendSettings.dvbs(frontendDvbsSettings); return frontendSettings; } static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendSettings"); FrontendDvbtTransmissionMode transmissionMode = static_cast( env->GetIntField( settings, env->GetFieldID(clazz, "mTransmissionMode", "I"))); FrontendDvbtBandwidth bandwidth = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I"))); FrontendDvbtConstellation constellation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mConstellation", "I"))); FrontendDvbtHierarchy hierarchy = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mHierarchy", "I"))); FrontendDvbtCoderate hpCoderate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mHpCodeRate", "I"))); FrontendDvbtCoderate lpCoderate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mLpCodeRate", "I"))); FrontendDvbtGuardInterval guardInterval = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I"))); bool isHighPriority = static_cast( env->GetBooleanField( settings, env->GetFieldID(clazz, "mIsHighPriority", "Z"))); FrontendDvbtStandard standard = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mStandard", "I"))); bool isMiso = static_cast( env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsMiso", "Z"))); FrontendDvbtPlpMode plpMode = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mPlpMode", "I"))); uint8_t plpId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mPlpId", "I"))); uint8_t plpGroupId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mPlpGroupId", "I"))); FrontendDvbtSettings frontendDvbtSettings { .frequency = freq, .transmissionMode = transmissionMode, .bandwidth = bandwidth, .constellation = constellation, .hierarchy = hierarchy, .hpCoderate = hpCoderate, .lpCoderate = lpCoderate, .guardInterval = guardInterval, .isHighPriority = isHighPriority, .standard = standard, .isMiso = isMiso, .plpMode = plpMode, .plpId = plpId, .plpGroupId = plpGroupId, }; frontendSettings.dvbt(frontendDvbtSettings); return frontendSettings; } static FrontendSettings getIsdbsFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendSettings"); uint16_t streamId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I"))); FrontendIsdbsStreamIdType streamIdType = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mStreamIdType", "I"))); FrontendIsdbsModulation modulation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); FrontendIsdbsCoderate coderate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I"))); uint32_t symbolRate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I"))); FrontendIsdbsRolloff rolloff = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mRolloff", "I"))); FrontendIsdbsSettings frontendIsdbsSettings { .frequency = freq, .streamId = streamId, .streamIdType = streamIdType, .modulation = modulation, .coderate = coderate, .symbolRate = symbolRate, .rolloff = rolloff, }; frontendSettings.isdbs(frontendIsdbsSettings); return frontendSettings; } static FrontendSettings getIsdbs3FrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendSettings"); uint16_t streamId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I"))); FrontendIsdbsStreamIdType streamIdType = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mStreamIdType", "I"))); FrontendIsdbs3Modulation modulation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); FrontendIsdbs3Coderate coderate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I"))); uint32_t symbolRate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I"))); FrontendIsdbs3Rolloff rolloff = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mRolloff", "I"))); FrontendIsdbs3Settings frontendIsdbs3Settings { .frequency = freq, .streamId = streamId, .streamIdType = streamIdType, .modulation = modulation, .coderate = coderate, .symbolRate = symbolRate, .rolloff = rolloff, }; frontendSettings.isdbs3(frontendIsdbs3Settings); return frontendSettings; } static FrontendSettings getIsdbtFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendSettings"); FrontendIsdbtModulation modulation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); FrontendIsdbtBandwidth bandwidth = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I"))); FrontendIsdbtMode mode = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mMode", "I"))); FrontendIsdbtCoderate coderate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I"))); FrontendIsdbtGuardInterval guardInterval = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I"))); uint32_t serviceAreaId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mServiceAreaId", "I"))); FrontendIsdbtSettings frontendIsdbtSettings { .frequency = freq, .modulation = modulation, .bandwidth = bandwidth, .mode = mode, .coderate = coderate, .guardInterval = guardInterval, .serviceAreaId = serviceAreaId, }; frontendSettings.isdbt(frontendIsdbtSettings); return frontendSettings; } static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) { ALOGD("getFrontendSettings %d", type); FrontendType feType = static_cast(type); switch(feType) { case FrontendType::ANALOG: return getAnalogFrontendSettings(env, settings); case FrontendType::ATSC3: return getAtsc3FrontendSettings(env, settings); case FrontendType::ATSC: return getAtscFrontendSettings(env, settings); case FrontendType::DVBC: return getDvbcFrontendSettings(env, settings); case FrontendType::DVBS: return getDvbsFrontendSettings(env, settings); case FrontendType::DVBT: return getDvbtFrontendSettings(env, settings); case FrontendType::ISDBS: return getIsdbsFrontendSettings(env, settings); case FrontendType::ISDBS3: return getIsdbs3FrontendSettings(env, settings); case FrontendType::ISDBT: return getIsdbtFrontendSettings(env, settings); default: // should never happen because a type is associated with a subclass of // FrontendSettings and not set by users jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Unsupported frontend type %d", type); return FrontendSettings(); } } static sp getFilter(JNIEnv *env, jobject filter) { return (Filter *)env->GetLongField(filter, gFields.filterContext); } static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder) { DvrSettings dvrSettings; jclass clazz = env->FindClass("android/media/tv/tuner/dvr/DvrSettings"); uint32_t statusMask = static_cast(env->GetIntField( settings, env->GetFieldID(clazz, "mStatusMask", "I"))); uint32_t lowThreshold = static_cast(env->GetLongField( settings, env->GetFieldID(clazz, "mLowThreshold", "J"))); uint32_t highThreshold = static_cast(env->GetLongField( settings, env->GetFieldID(clazz, "mHighThreshold", "J"))); uint8_t packetSize = static_cast(env->GetLongField( settings, env->GetFieldID(clazz, "mPacketSize", "J"))); DataFormat dataFormat = static_cast(env->GetIntField( settings, env->GetFieldID(clazz, "mDataFormat", "I"))); if (isRecorder) { RecordSettings recordSettings { .statusMask = static_cast(statusMask), .lowThreshold = lowThreshold, .highThreshold = highThreshold, .dataFormat = dataFormat, .packetSize = packetSize, }; dvrSettings.record(recordSettings); } else { PlaybackSettings PlaybackSettings { .statusMask = statusMask, .lowThreshold = lowThreshold, .highThreshold = highThreshold, .dataFormat = dataFormat, .packetSize = packetSize, }; dvrSettings.playback(PlaybackSettings); } return dvrSettings; } static sp getDvr(JNIEnv *env, jobject dvr) { bool isRecorder = env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder")); jfieldID fieldId = isRecorder ? gFields.dvrRecorderContext : gFields.dvrPlaybackContext; return (Dvr *)env->GetLongField(dvr, fieldId); } static void android_media_tv_Tuner_native_init(JNIEnv *env) { jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); CHECK(clazz != NULL); gFields.tunerContext = env->GetFieldID(clazz, "mNativeContext", "J"); CHECK(gFields.tunerContext != NULL); gFields.onFrontendEventID = env->GetMethodID(clazz, "onFrontendEvent", "(I)V"); jclass frontendClazz = env->FindClass("android/media/tv/tuner/Tuner$Frontend"); gFields.frontendInitID = env->GetMethodID(frontendClazz, "", "(Landroid/media/tv/tuner/Tuner;I)V"); jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb"); gFields.lnbContext = env->GetFieldID(lnbClazz, "mNativeContext", "J"); gFields.lnbInitID = env->GetMethodID(lnbClazz, "", "(I)V"); gFields.onLnbEventID = env->GetMethodID(lnbClazz, "onEvent", "(I)V"); gFields.onLnbDiseqcMessageID = env->GetMethodID(lnbClazz, "onDiseqcMessage", "([B)V"); jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter"); gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J"); gFields.filterInitID = env->GetMethodID(filterClazz, "", "(I)V"); gFields.onFilterStatusID = env->GetMethodID(filterClazz, "onFilterStatus", "(I)V"); gFields.onFilterEventID = env->GetMethodID(filterClazz, "onFilterEvent", "([Landroid/media/tv/tuner/filter/FilterEvent;)V"); jclass timeFilterClazz = env->FindClass("android/media/tv/tuner/filter/TimeFilter"); gFields.timeFilterContext = env->GetFieldID(timeFilterClazz, "mNativeContext", "J"); gFields.timeFilterInitID = env->GetMethodID(timeFilterClazz, "", "()V"); jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Descrambler"); gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J"); gFields.descramblerInitID = env->GetMethodID(descramblerClazz, "", "()V"); jclass dvrRecorderClazz = env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"); gFields.dvrRecorderContext = env->GetFieldID(dvrRecorderClazz, "mNativeContext", "J"); gFields.dvrRecorderInitID = env->GetMethodID(dvrRecorderClazz, "", "()V"); gFields.onDvrRecordStatusID = env->GetMethodID(dvrRecorderClazz, "onRecordStatusChanged", "(I)V"); jclass dvrPlaybackClazz = env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"); gFields.dvrPlaybackContext = env->GetFieldID(dvrPlaybackClazz, "mNativeContext", "J"); gFields.dvrPlaybackInitID = env->GetMethodID(dvrPlaybackClazz, "", "()V"); gFields.onDvrPlaybackStatusID = env->GetMethodID(dvrPlaybackClazz, "onPlaybackStatusChanged", "(I)V"); jclass mediaEventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent"); gFields.mediaEventContext = env->GetFieldID(mediaEventClazz, "mNativeContext", "J"); jclass linearBlockClazz = env->FindClass("android/media/MediaCodec$LinearBlock"); gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "", "()V"); gFields.linearBlockSetInternalStateID = env->GetMethodID(linearBlockClazz, "setInternalStateLocked", "(JZ)V"); } static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) { sp tuner = new JTuner(env, thiz); setTuner(env,thiz, tuner); } static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->getFrontendIds(); } static jobject android_media_tv_Tuner_open_frontend_by_handle( JNIEnv *env, jobject thiz, jint handle) { sp tuner = getTuner(env, thiz); uint32_t id = getResourceIdFromHandle(handle); return tuner->openFrontendById(id); } static jint android_media_tv_Tuner_close_frontend_by_handle( JNIEnv *env, jobject thiz, jint handle) { sp tuner = getTuner(env, thiz); uint32_t id = getResourceIdFromHandle(handle); return tuner->closeFrontendById(id); } static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) { sp tuner = getTuner(env, thiz); return tuner->tune(getFrontendSettings(env, type, settings)); } static int android_media_tv_Tuner_stop_tune(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->stopTune(); } static int android_media_tv_Tuner_scan( JNIEnv *env, jobject thiz, jint settingsType, jobject settings, jint scanType) { sp tuner = getTuner(env, thiz); return tuner->scan(getFrontendSettings( env, settingsType, settings), static_cast(scanType)); } static int android_media_tv_Tuner_stop_scan(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->stopScan(); } static int android_media_tv_Tuner_set_lnb(JNIEnv *env, jobject thiz, jint id) { sp tuner = getTuner(env, thiz); return tuner->setLnb(id); } static int android_media_tv_Tuner_set_lna(JNIEnv *env, jobject thiz, jboolean enable) { sp tuner = getTuner(env, thiz); return tuner->setLna(enable); } static jobject android_media_tv_Tuner_get_frontend_status( JNIEnv* env, jobject thiz, jintArray types) { sp tuner = getTuner(env, thiz); return tuner->getFrontendStatus(types); } static jobject android_media_tv_Tuner_get_av_sync_hw_id( JNIEnv *env, jobject thiz, jobject filter) { sp filterSp = getFilter(env, filter); if (filterSp == NULL) { ALOGD("Failed to get sync ID. Filter not found"); return NULL; } sp tuner = getTuner(env, thiz); return tuner->getAvSyncHwId(filterSp); } static jobject android_media_tv_Tuner_get_av_sync_time(JNIEnv *env, jobject thiz, jint id) { sp tuner = getTuner(env, thiz); return tuner->getAvSyncTime(id); } static int android_media_tv_Tuner_connect_cicam(JNIEnv *env, jobject thiz, jint id) { sp tuner = getTuner(env, thiz); return tuner->connectCiCam(id); } static int android_media_tv_Tuner_disconnect_cicam(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->disconnectCiCam(); } static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thiz, jint id) { sp tuner = getTuner(env, thiz); return tuner->getFrontendInfo(id); } static jintArray android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->getLnbIds(); } static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) { sp tuner = getTuner(env, thiz); uint32_t id = getResourceIdFromHandle(handle); return tuner->openLnbById(id); } static jobject android_media_tv_Tuner_open_lnb_by_name(JNIEnv *env, jobject thiz, jstring name) { sp tuner = getTuner(env, thiz); return tuner->openLnbByName(name); } static jobject android_media_tv_Tuner_open_filter( JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) { sp tuner = getTuner(env, thiz); DemuxFilterMainType mainType = static_cast(type); DemuxFilterType filterType { .mainType = mainType, }; switch(mainType) { case DemuxFilterMainType::TS: filterType.subType.tsFilterType(static_cast(subType)); break; case DemuxFilterMainType::MMTP: filterType.subType.mmtpFilterType(static_cast(subType)); break; case DemuxFilterMainType::IP: filterType.subType.ipFilterType(static_cast(subType)); break; case DemuxFilterMainType::TLV: filterType.subType.tlvFilterType(static_cast(subType)); break; case DemuxFilterMainType::ALP: filterType.subType.alpFilterType(static_cast(subType)); break; } return tuner->openFilter(filterType, bufferSize); } static jobject android_media_tv_Tuner_open_time_filter(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->openTimeFilter(); } static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"); jbyteArray jfilterBytes = static_cast( env->GetObjectField(settings, env->GetFieldID(clazz, "mFilter", "[B"))); jsize size = env->GetArrayLength(jfilterBytes); std::vector filterBytes(size); env->GetByteArrayRegion( jfilterBytes, 0, size, reinterpret_cast(&filterBytes[0])); jbyteArray jmask = static_cast( env->GetObjectField(settings, env->GetFieldID(clazz, "mMask", "[B"))); size = env->GetArrayLength(jmask); std::vector mask(size); env->GetByteArrayRegion(jmask, 0, size, reinterpret_cast(&mask[0])); jbyteArray jmode = static_cast( env->GetObjectField(settings, env->GetFieldID(clazz, "mMode", "[B"))); size = env->GetArrayLength(jmode); std::vector mode(size); env->GetByteArrayRegion(jmode, 0, size, reinterpret_cast(&mode[0])); DemuxFilterSectionBits filterSectionBits { .filter = filterBytes, .mask = mask, .mode = mode, }; return filterSectionBits; } static DemuxFilterSectionSettings::Condition::TableInfo getFilterTableInfo( JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"); uint16_t tableId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mTableId", "I"))); uint16_t version = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mVersion", "I"))); DemuxFilterSectionSettings::Condition::TableInfo tableInfo { .tableId = tableId, .version = version, }; return tableInfo; } static DemuxFilterSectionSettings getFilterSectionSettings(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettings"); bool isCheckCrc = static_cast( env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z"))); bool isRepeat = static_cast( env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z"))); bool isRaw = static_cast( env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"))); DemuxFilterSectionSettings filterSectionSettings { .isCheckCrc = isCheckCrc, .isRepeat = isRepeat, .isRaw = isRaw, }; if (env->IsInstanceOf( settings, env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"))) { filterSectionSettings.condition.sectionBits(getFilterSectionBits(env, settings)); } else if (env->IsInstanceOf( settings, env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"))) { filterSectionSettings.condition.tableInfo(getFilterTableInfo(env, settings)); } return filterSectionSettings; } static DemuxFilterAvSettings getFilterAvSettings(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings"); bool isPassthrough = static_cast( env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z"))); DemuxFilterAvSettings filterAvSettings { .isPassthrough = isPassthrough, }; return filterAvSettings; } static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings"); uint16_t streamId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I"))); bool isRaw = static_cast( env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"))); DemuxFilterPesDataSettings filterPesDataSettings { .streamId = streamId, .isRaw = isRaw, }; return filterPesDataSettings; } static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/RecordSettings"); hidl_bitfield tsIndexMask = static_cast>( env->GetIntField(settings, env->GetFieldID(clazz, "mTsIndexMask", "I"))); DemuxRecordScIndexType scIndexType = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexType", "I"))); jint scIndexMask = env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexMask", "I")); DemuxFilterRecordSettings filterRecordSettings { .tsIndexMask = tsIndexMask, .scIndexType = scIndexType, }; if (scIndexType == DemuxRecordScIndexType::SC) { filterRecordSettings.scIndexMask.sc(static_cast>(scIndexMask)); } else if (scIndexType == DemuxRecordScIndexType::SC_HEVC) { filterRecordSettings.scIndexMask.scHevc( static_cast>(scIndexMask)); } return filterRecordSettings; } static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings"); uint32_t downloadId = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I"))); DemuxFilterDownloadSettings filterDownloadSettings { .downloadId = downloadId, }; return filterDownloadSettings; } static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/IpFilterConfiguration"); jbyteArray jsrcIpAddress = static_cast( env->GetObjectField(config, env->GetFieldID(clazz, "mSrcIpAddress", "[B"))); jsize srcSize = env->GetArrayLength(jsrcIpAddress); jbyteArray jdstIpAddress = static_cast( env->GetObjectField(config, env->GetFieldID(clazz, "mDstIpAddress", "[B"))); jsize dstSize = env->GetArrayLength(jdstIpAddress); DemuxIpAddress res; if (srcSize != dstSize) { // should never happen. Validated on Java size. jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "IP address lengths don't match. srcLength=%d, dstLength=%d", srcSize, dstSize); return res; } if (srcSize == IP_V4_LENGTH) { uint8_t srcAddr[IP_V4_LENGTH]; uint8_t dstAddr[IP_V4_LENGTH]; env->GetByteArrayRegion( jsrcIpAddress, 0, srcSize, reinterpret_cast(srcAddr)); env->GetByteArrayRegion( jdstIpAddress, 0, dstSize, reinterpret_cast(dstAddr)); res.srcIpAddress.v4(srcAddr); res.dstIpAddress.v4(dstAddr); } else if (srcSize == IP_V6_LENGTH) { uint8_t srcAddr[IP_V6_LENGTH]; uint8_t dstAddr[IP_V6_LENGTH]; env->GetByteArrayRegion( jsrcIpAddress, 0, srcSize, reinterpret_cast(srcAddr)); env->GetByteArrayRegion( jdstIpAddress, 0, dstSize, reinterpret_cast(dstAddr)); res.srcIpAddress.v6(srcAddr); res.dstIpAddress.v6(dstAddr); } else { // should never happen. Validated on Java size. jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid IP address length %d", srcSize); return res; } uint16_t srcPort = static_cast( env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I"))); uint16_t dstPort = static_cast( env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I"))); res.srcPort = srcPort; res.dstPort = dstPort; return res; } static DemuxFilterSettings getFilterConfiguration( JNIEnv *env, int type, int subtype, jobject filterConfigObj) { DemuxFilterSettings filterSettings; jobject settingsObj = env->GetObjectField( filterConfigObj, env->GetFieldID( env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"), "mSettings", "Landroid/media/tv/tuner/filter/Settings;")); DemuxFilterMainType mainType = static_cast(type); switch (mainType) { case DemuxFilterMainType::TS: { jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration"); uint16_t tpid = static_cast( env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mTpid", "I"))); DemuxTsFilterSettings tsFilterSettings { .tpid = tpid, }; DemuxTsFilterType tsType = static_cast(subtype); switch (tsType) { case DemuxTsFilterType::SECTION: tsFilterSettings.filterSettings.section( getFilterSectionSettings(env, settingsObj)); break; case DemuxTsFilterType::AUDIO: case DemuxTsFilterType::VIDEO: tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); break; case DemuxTsFilterType::PES: tsFilterSettings.filterSettings.pesData( getFilterPesDataSettings(env, settingsObj)); break; case DemuxTsFilterType::RECORD: tsFilterSettings.filterSettings.record( getFilterRecordSettings(env, settingsObj)); break; default: break; } filterSettings.ts(tsFilterSettings); break; } case DemuxFilterMainType::MMTP: { jclass clazz = env->FindClass("android/media/tv/tuner/filter/MmtpFilterConfiguration"); uint16_t mmtpPid = static_cast( env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mMmtpPid", "I"))); DemuxMmtpFilterSettings mmtpFilterSettings { .mmtpPid = mmtpPid, }; DemuxMmtpFilterType mmtpType = static_cast(subtype); switch (mmtpType) { case DemuxMmtpFilterType::SECTION: mmtpFilterSettings.filterSettings.section( getFilterSectionSettings(env, settingsObj)); break; case DemuxMmtpFilterType::AUDIO: case DemuxMmtpFilterType::VIDEO: mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); break; case DemuxMmtpFilterType::PES: mmtpFilterSettings.filterSettings.pesData( getFilterPesDataSettings(env, settingsObj)); break; case DemuxMmtpFilterType::RECORD: mmtpFilterSettings.filterSettings.record( getFilterRecordSettings(env, settingsObj)); break; case DemuxMmtpFilterType::DOWNLOAD: mmtpFilterSettings.filterSettings.download( getFilterDownloadSettings(env, settingsObj)); break; default: break; } filterSettings.mmtp(mmtpFilterSettings); break; } case DemuxFilterMainType::IP: { DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj); DemuxIpFilterSettings ipFilterSettings { .ipAddr = ipAddr, }; DemuxIpFilterType ipType = static_cast(subtype); switch (ipType) { case DemuxIpFilterType::SECTION: { ipFilterSettings.filterSettings.section( getFilterSectionSettings(env, settingsObj)); break; } case DemuxIpFilterType::IP: { jclass clazz = env->FindClass( "android/media/tv/tuner/filter/IpFilterConfiguration"); bool bPassthrough = static_cast( env->GetBooleanField( filterConfigObj, env->GetFieldID( clazz, "mPassthrough", "Z"))); ipFilterSettings.filterSettings.bPassthrough(bPassthrough); break; } default: { break; } } filterSettings.ip(ipFilterSettings); break; } case DemuxFilterMainType::TLV: { jclass clazz = env->FindClass("android/media/tv/tuner/filter/TlvFilterConfiguration"); uint8_t packetType = static_cast( env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I"))); bool isCompressedIpPacket = static_cast( env->GetBooleanField( filterConfigObj, env->GetFieldID(clazz, "mIsCompressedIpPacket", "Z"))); DemuxTlvFilterSettings tlvFilterSettings { .packetType = packetType, .isCompressedIpPacket = isCompressedIpPacket, }; DemuxTlvFilterType tlvType = static_cast(subtype); switch (tlvType) { case DemuxTlvFilterType::SECTION: { tlvFilterSettings.filterSettings.section( getFilterSectionSettings(env, settingsObj)); break; } case DemuxTlvFilterType::TLV: { bool bPassthrough = static_cast( env->GetBooleanField( filterConfigObj, env->GetFieldID( clazz, "mPassthrough", "Z"))); tlvFilterSettings.filterSettings.bPassthrough(bPassthrough); break; } default: { break; } } filterSettings.tlv(tlvFilterSettings); break; } case DemuxFilterMainType::ALP: { jclass clazz = env->FindClass("android/media/tv/tuner/filter/AlpFilterConfiguration"); uint8_t packetType = static_cast( env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I"))); DemuxAlpLengthType lengthType = static_cast( env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mLengthType", "I"))); DemuxAlpFilterSettings alpFilterSettings { .packetType = packetType, .lengthType = lengthType, }; DemuxAlpFilterType alpType = static_cast(subtype); switch (alpType) { case DemuxAlpFilterType::SECTION: alpFilterSettings.filterSettings.section( getFilterSectionSettings(env, settingsObj)); break; default: break; } filterSettings.alp(alpFilterSettings); break; } default: { break; } } return filterSettings; } static jint copyData(JNIEnv *env, std::unique_ptr& mq, EventFlag* flag, jbyteArray buffer, jlong offset, jlong size) { ALOGD("copyData, size=%ld, offset=%ld", (long) size, (long) offset); jlong available = mq->availableToRead(); ALOGD("copyData, available=%ld", (long) available); size = std::min(size, available); jboolean isCopy; jbyte *dst = env->GetByteArrayElements(buffer, &isCopy); ALOGD("copyData, isCopy=%d", isCopy); if (dst == nullptr) { jniThrowRuntimeException(env, "Failed to GetByteArrayElements"); return 0; } if (mq->read(reinterpret_cast(dst) + offset, size)) { env->ReleaseByteArrayElements(buffer, dst, 0); flag->wake(static_cast(DemuxQueueNotifyBits::DATA_CONSUMED)); } else { jniThrowRuntimeException(env, "Failed to read FMQ"); env->ReleaseByteArrayElements(buffer, dst, 0); return 0; } return size; } static jint 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 filterSp = getFilter(env, filter); sp iFilterSp = filterSp->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to configure filter: filter not found"); return (jint) Result::NOT_INITIALIZED; } DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings); Result res = iFilterSp->configure(filterSettings); if (res != Result::SUCCESS) { return (jint) res; } MQDescriptorSync filterMQDesc; Result getQueueDescResult = Result::UNKNOWN_ERROR; if (filterSp->mFilterMQ == NULL) { iFilterSp->getQueueDesc( [&](Result r, const MQDescriptorSync& desc) { filterMQDesc = desc; getQueueDescResult = r; ALOGD("getFilterQueueDesc"); }); if (getQueueDescResult == Result::SUCCESS) { filterSp->mFilterMQ = std::make_unique(filterMQDesc, true); EventFlag::createEventFlag( filterSp->mFilterMQ->getEventFlagWord(), &(filterSp->mFilterMQEventFlag)); } } return (jint) getQueueDescResult; } static jint android_media_tv_Tuner_get_filter_id(JNIEnv* env, jobject filter) { sp iFilterSp = getFilter(env, filter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to get filter ID: filter not found"); return (int) Result::NOT_INITIALIZED; } Result res; uint32_t id; iFilterSp->getId( [&](Result r, uint32_t filterId) { res = r; id = filterId; }); if (res != Result::SUCCESS) { return (jint) Constant::INVALID_FILTER_ID; } return (jint) id; } static jint android_media_tv_Tuner_set_filter_data_source( JNIEnv* env, jobject filter, jobject srcFilter) { sp iFilterSp = getFilter(env, filter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to set filter data source: filter not found"); return (jint) Result::NOT_INITIALIZED; } Result r; if (srcFilter == NULL) { r = iFilterSp->setDataSource(NULL); } else { sp srcSp = getFilter(env, srcFilter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to set filter data source: src filter not found"); return (jint) Result::INVALID_ARGUMENT; } r = iFilterSp->setDataSource(srcSp); } return (jint) r; } static jint android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) { sp iFilterSp = getFilter(env, filter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to start filter: filter not found"); return (jint) Result::NOT_INITIALIZED; } Result r = iFilterSp->start(); return (jint) r; } static jint android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) { sp iFilterSp = getFilter(env, filter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to stop filter: filter not found"); return (jint) Result::NOT_INITIALIZED; } Result r = iFilterSp->stop(); return (jint) r; } static jint android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) { sp iFilterSp = getFilter(env, filter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to flush filter: filter not found"); return (jint) Result::NOT_INITIALIZED; } Result r = iFilterSp->flush(); return (jint) r; } static jint android_media_tv_Tuner_read_filter_fmq( JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) { sp filterSp = getFilter(env, filter); if (filterSp == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to read filter FMQ: filter not found"); return 0; } return copyData(env, filterSp->mFilterMQ, filterSp->mFilterMQEventFlag, buffer, offset, size); } static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) { sp iFilterSp = getFilter(env, filter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to close filter: filter not found"); return (jint) Result::NOT_INITIALIZED; } Result r = iFilterSp->close(); return (jint) r; } static sp getTimeFilter(JNIEnv *env, jobject filter) { return (TimeFilter *)env->GetLongField(filter, gFields.timeFilterContext); } static int android_media_tv_Tuner_time_filter_set_timestamp( JNIEnv *env, jobject filter, jlong timestamp) { sp filterSp = getTimeFilter(env, filter); if (filterSp == NULL) { ALOGD("Failed set timestamp: time filter not found"); return (int) Result::INVALID_STATE; } sp iFilterSp = filterSp->getITimeFilter(); Result r = iFilterSp->setTimeStamp(static_cast(timestamp)); return (int) r; } static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv *env, jobject filter) { sp filterSp = getTimeFilter(env, filter); if (filterSp == NULL) { ALOGD("Failed clear timestamp: time filter not found"); return (int) Result::INVALID_STATE; } sp iFilterSp = filterSp->getITimeFilter(); Result r = iFilterSp->clearTimeStamp(); return (int) r; } static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, jobject filter) { sp filterSp = getTimeFilter(env, filter); if (filterSp == NULL) { ALOGD("Failed get timestamp: time filter not found"); return NULL; } sp iFilterSp = filterSp->getITimeFilter(); Result res; uint64_t timestamp; iFilterSp->getTimeStamp( [&](Result r, uint64_t t) { res = r; timestamp = t; }); if (res != Result::SUCCESS) { return NULL; } jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); jobject longObj = env->NewObject(longClazz, longInit, static_cast(timestamp)); return longObj; } static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, jobject filter) { sp filterSp = getTimeFilter(env, filter); if (filterSp == NULL) { ALOGD("Failed get source time: time filter not found"); return NULL; } sp iFilterSp = filterSp->getITimeFilter(); Result res; uint64_t timestamp; iFilterSp->getSourceTime( [&](Result r, uint64_t t) { res = r; timestamp = t; }); if (res != Result::SUCCESS) { return NULL; } jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); jobject longObj = env->NewObject(longClazz, longInit, static_cast(timestamp)); return longObj; } static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter) { sp filterSp = getTimeFilter(env, filter); if (filterSp == NULL) { ALOGD("Failed close time filter: time filter not found"); return (int) Result::INVALID_STATE; } Result r = filterSp->getITimeFilter()->close(); if (r == Result::SUCCESS) { filterSp->decStrong(filter); env->SetLongField(filter, gFields.timeFilterContext, 0); } return (int) r; } static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz, jint) { sp tuner = getTuner(env, thiz); return tuner->openDescrambler(); } static jint android_media_tv_Tuner_descrambler_add_pid( JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) { sp descramblerSp = getDescrambler(env, descrambler); if (descramblerSp == NULL) { return (jint) Result::NOT_INITIALIZED; } sp iFilterSp = getFilter(env, filter)->getIFilter(); Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), iFilterSp); return (jint) result; } static jint android_media_tv_Tuner_descrambler_remove_pid( JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) { sp descramblerSp = getDescrambler(env, descrambler); if (descramblerSp == NULL) { return (jint) Result::NOT_INITIALIZED; } sp iFilterSp = getFilter(env, filter)->getIFilter(); Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), iFilterSp); return (jint) result; } static jint android_media_tv_Tuner_descrambler_set_key_token( JNIEnv* env, jobject descrambler, jbyteArray keyToken) { sp descramblerSp = getDescrambler(env, descrambler); if (descramblerSp == NULL) { return (jint) Result::NOT_INITIALIZED; } int size = env->GetArrayLength(keyToken); std::vector v(size); env->GetByteArrayRegion(keyToken, 0, size, reinterpret_cast(&v[0])); Result result = descramblerSp->setKeyToken(v); return (jint) result; } static jint android_media_tv_Tuner_close_descrambler(JNIEnv* env, jobject descrambler) { sp descramblerSp = getDescrambler(env, descrambler); if (descramblerSp == NULL) { return (jint) Result::NOT_INITIALIZED; } Result r = descramblerSp->close(); if (r == Result::SUCCESS) { descramblerSp->decStrong(descrambler); } return (jint) r; } static jobject android_media_tv_Tuner_open_dvr_recorder( JNIEnv* env, jobject thiz, jlong bufferSize) { sp tuner = getTuner(env, thiz); return tuner->openDvr(DvrType::RECORD, bufferSize); } static jobject android_media_tv_Tuner_open_dvr_playback( JNIEnv* env, jobject thiz, jlong bufferSize) { sp tuner = getTuner(env, thiz); return tuner->openDvr(DvrType::PLAYBACK, bufferSize); } static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->getDemuxCaps(); } static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint /* handle */) { sp tuner = getTuner(env, thiz); return (jint) tuner->openDemux(); } static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) { sp tuner = getTuner(env, thiz); return (jint) tuner->close(); } static jint android_media_tv_Tuner_close_demux(JNIEnv* env, jobject thiz, jint /* handle */) { sp tuner = getTuner(env, thiz); return tuner->closeDemux(); } static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) { sp tuner = getTuner(env, thiz); return tuner->closeFrontend(); } static jint android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { return (jint) Result::NOT_INITIALIZED; } sp filterSp = getFilter(env, filter); if (filterSp == NULL) { return (jint) Result::INVALID_ARGUMENT; } sp iDvrSp = dvrSp->getIDvr(); sp iFilterSp = filterSp->getIFilter(); Result result = iDvrSp->attachFilter(iFilterSp); return (jint) result; } static jint android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { return (jint) Result::NOT_INITIALIZED; } sp filterSp = getFilter(env, filter); if (filterSp == NULL) { return (jint) Result::INVALID_ARGUMENT; } sp iDvrSp = dvrSp->getIDvr(); sp iFilterSp = filterSp->getIFilter(); Result result = iDvrSp->detachFilter(iFilterSp); return (jint) result; } static jint android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { ALOGD("Failed to configure dvr: dvr not found"); return (int)Result::NOT_INITIALIZED; } sp iDvrSp = dvrSp->getIDvr(); bool isRecorder = env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder")); Result result = iDvrSp->configure(getDvrSettings(env, settings, isRecorder)); if (result != Result::SUCCESS) { return (jint) result; } MQDescriptorSync dvrMQDesc; Result getQueueDescResult = Result::UNKNOWN_ERROR; iDvrSp->getQueueDesc( [&](Result r, const MQDescriptorSync& desc) { dvrMQDesc = desc; getQueueDescResult = r; ALOGD("getDvrQueueDesc"); }); if (getQueueDescResult == Result::SUCCESS) { dvrSp->mDvrMQ = std::make_unique(dvrMQDesc, true); EventFlag::createEventFlag( dvrSp->mDvrMQ->getEventFlagWord(), &(dvrSp->mDvrMQEventFlag)); } return (jint) getQueueDescResult; } static jint android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { ALOGD("Failed to start dvr: dvr not found"); return (jint) Result::NOT_INITIALIZED; } sp iDvrSp = dvrSp->getIDvr(); Result result = iDvrSp->start(); return (jint) result; } static jint android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { ALOGD("Failed to stop dvr: dvr not found"); return (jint) Result::NOT_INITIALIZED; } sp iDvrSp = dvrSp->getIDvr(); Result result = iDvrSp->stop(); return (jint) result; } static jint android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { ALOGD("Failed to flush dvr: dvr not found"); return (jint) Result::NOT_INITIALIZED; } sp iDvrSp = dvrSp->getIDvr(); Result result = iDvrSp->flush(); return (jint) result; } static jint android_media_tv_Tuner_close_dvr(JNIEnv* env, jobject dvr) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { ALOGD("Failed to close dvr: dvr not found"); return (jint) Result::NOT_INITIALIZED; } return dvrSp->close(); } static sp getLnb(JNIEnv *env, jobject lnb) { return (Lnb *)env->GetLongField(lnb, gFields.lnbContext); } static jint android_media_tv_Tuner_lnb_set_voltage(JNIEnv* env, jobject lnb, jint voltage) { sp iLnbSp = getLnb(env, lnb)->getILnb(); Result r = iLnbSp->setVoltage(static_cast(voltage)); return (jint) r; } static int android_media_tv_Tuner_lnb_set_tone(JNIEnv* env, jobject lnb, jint tone) { sp iLnbSp = getLnb(env, lnb)->getILnb(); Result r = iLnbSp->setTone(static_cast(tone)); return (jint) r; } static int android_media_tv_Tuner_lnb_set_position(JNIEnv* env, jobject lnb, jint position) { sp iLnbSp = getLnb(env, lnb)->getILnb(); Result r = iLnbSp->setSatellitePosition(static_cast(position)); return (jint) r; } static int android_media_tv_Tuner_lnb_send_diseqc_msg(JNIEnv* env, jobject lnb, jbyteArray msg) { sp iLnbSp = getLnb(env, lnb)->getILnb(); int size = env->GetArrayLength(msg); std::vector v(size); env->GetByteArrayRegion(msg, 0, size, reinterpret_cast(&v[0])); Result r = iLnbSp->sendDiseqcMessage(v); return (jint) r; } static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) { sp lnbSp = getLnb(env, lnb); Result r = lnbSp->getILnb()->close(); if (r == Result::SUCCESS) { lnbSp->decStrong(lnb); env->SetLongField(lnb, gFields.lnbContext, 0); } return (jint) r; } static void android_media_tv_Tuner_dvr_set_fd(JNIEnv *env, jobject dvr, jint fd) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { ALOGD("Failed to set FD for dvr: dvr not found"); } dvrSp->mFd = (int) fd; ALOGD("set fd = %d", dvrSp->mFd); } static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong size) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to read dvr: dvr not found"); return 0; } long available = dvrSp->mDvrMQ->availableToWrite(); long write = std::min((long) size, available); MQ::MemTransaction tx; long ret = 0; if (dvrSp->mDvrMQ->beginWrite(write, &tx)) { auto first = tx.getFirstRegion(); auto data = first.getAddress(); long length = first.getLength(); long firstToWrite = std::min(length, write); ret = read(dvrSp->mFd, data, firstToWrite); if (ret < 0) { ALOGE("[DVR] Failed to read from FD: %s", strerror(errno)); jniThrowRuntimeException(env, strerror(errno)); return 0; } if (ret < firstToWrite) { ALOGW("[DVR] file to MQ, first region: %ld bytes to write, but %ld bytes written", firstToWrite, ret); } else if (firstToWrite < write) { ALOGD("[DVR] write second region: %ld bytes written, %ld bytes in total", ret, write); auto second = tx.getSecondRegion(); data = second.getAddress(); length = second.getLength(); int secondToWrite = std::min(length, write - firstToWrite); ret += read(dvrSp->mFd, data, secondToWrite); } ALOGD("[DVR] file to MQ: %ld bytes need to be written, %ld bytes written", write, ret); if (!dvrSp->mDvrMQ->commitWrite(ret)) { ALOGE("[DVR] Error: failed to commit write!"); return 0; } } else { ALOGE("dvrMq.beginWrite failed"); } return (jlong) ret; } static jlong android_media_tv_Tuner_read_dvr_from_array( JNIEnv* env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { ALOGW("Failed to read dvr: dvr not found"); return 0; } if (dvrSp->mDvrMQ == NULL) { ALOGW("Failed to read dvr: dvr not configured"); return 0; } jlong available = dvrSp->mDvrMQ->availableToWrite(); size = std::min(size, available); jboolean isCopy; jbyte *src = env->GetByteArrayElements(buffer, &isCopy); if (src == nullptr) { ALOGD("Failed to GetByteArrayElements"); return 0; } if (dvrSp->mDvrMQ->write(reinterpret_cast(src) + offset, size)) { env->ReleaseByteArrayElements(buffer, src, 0); dvrSp->mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_CONSUMED)); } else { ALOGD("Failed to write FMQ"); env->ReleaseByteArrayElements(buffer, src, 0); return 0; } return size; } static jlong android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jlong size) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to write dvr: dvr not found"); return 0; } if (dvrSp->mDvrMQ == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to write dvr: dvr not configured"); return 0; } MQ& dvrMq = dvrSp->getDvrMQ(); long available = dvrMq.availableToRead(); long toRead = std::min((long) size, available); long ret = 0; MQ::MemTransaction tx; if (dvrMq.beginRead(toRead, &tx)) { auto first = tx.getFirstRegion(); auto data = first.getAddress(); long length = first.getLength(); long firstToRead = std::min(length, toRead); ret = write(dvrSp->mFd, data, firstToRead); if (ret < 0) { ALOGE("[DVR] Failed to write to FD: %s", strerror(errno)); jniThrowRuntimeException(env, strerror(errno)); return 0; } if (ret < firstToRead) { ALOGW("[DVR] MQ to file: %ld bytes read, but %ld bytes written", firstToRead, ret); } else if (firstToRead < toRead) { ALOGD("[DVR] read second region: %ld bytes read, %ld bytes in total", ret, toRead); auto second = tx.getSecondRegion(); data = second.getAddress(); length = second.getLength(); int secondToRead = toRead - firstToRead; ret += write(dvrSp->mFd, data, secondToRead); } ALOGD("[DVR] MQ to file: %ld bytes to be read, %ld bytes written", toRead, ret); if (!dvrMq.commitRead(ret)) { ALOGE("[DVR] Error: failed to commit read!"); return 0; } } else { ALOGE("dvrMq.beginRead failed"); } return (jlong) ret; } static jlong android_media_tv_Tuner_write_dvr_to_array( JNIEnv *env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) { sp dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { ALOGW("Failed to write dvr: dvr not found"); return 0; } if (dvrSp->mDvrMQ == NULL) { ALOGW("Failed to write dvr: dvr not configured"); return 0; } return copyData(env, dvrSp->mDvrMQ, dvrSp->mDvrMQEventFlag, buffer, offset, size); } static sp getMediaEventSp(JNIEnv *env, jobject mediaEventObj) { return (MediaEvent *)env->GetLongField(mediaEventObj, gFields.mediaEventContext); } static jobject android_media_tv_Tuner_media_event_get_linear_block( JNIEnv* env, jobject mediaEventObj) { sp mediaEventSp = getMediaEventSp(env, mediaEventObj); if (mediaEventSp == NULL) { ALOGD("Failed get MediaEvent"); return NULL; } return mediaEventSp->getLinearBlock(); } static jobject android_media_tv_Tuner_media_event_get_audio_handle( JNIEnv* env, jobject mediaEventObj) { sp mediaEventSp = getMediaEventSp(env, mediaEventObj); if (mediaEventSp == NULL) { ALOGD("Failed get MediaEvent"); return NULL; } android::Mutex::Autolock autoLock(mediaEventSp->mLock); uint64_t audioHandle = mediaEventSp->getAudioHandle(); jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); jobject longObj = env->NewObject(longClazz, longInit, static_cast(audioHandle)); return longObj; } static void android_media_tv_Tuner_media_event_finalize(JNIEnv* env, jobject mediaEventObj) { sp mediaEventSp = getMediaEventSp(env, mediaEventObj); if (mediaEventSp == NULL) { ALOGD("Failed get MediaEvent"); return; } android::Mutex::Autolock autoLock(mediaEventSp->mLock); mediaEventSp->mAvHandleRefCnt--; mediaEventSp->finalize(); mediaEventSp->decStrong(mediaEventObj); } static const JNINativeMethod gTunerMethods[] = { { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init }, { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup }, { "nativeGetFrontendIds", "()Ljava/util/List;", (void *)android_media_tv_Tuner_get_frontend_ids }, { "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;", (void *)android_media_tv_Tuner_open_frontend_by_handle }, { "nativeCloseFrontendByHandle", "(I)I", (void *)android_media_tv_Tuner_close_frontend_by_handle }, { "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I", (void *)android_media_tv_Tuner_tune }, { "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune }, { "nativeScan", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;I)I", (void *)android_media_tv_Tuner_scan }, { "nativeStopScan", "()I", (void *)android_media_tv_Tuner_stop_scan }, { "nativeSetLnb", "(I)I", (void *)android_media_tv_Tuner_set_lnb }, { "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna }, { "nativeGetFrontendStatus", "([I)Landroid/media/tv/tuner/frontend/FrontendStatus;", (void *)android_media_tv_Tuner_get_frontend_status }, { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/filter/Filter;)Ljava/lang/Integer;", (void *)android_media_tv_Tuner_get_av_sync_hw_id }, { "nativeGetAvSyncTime", "(I)Ljava/lang/Long;", (void *)android_media_tv_Tuner_get_av_sync_time }, { "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam }, { "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam }, { "nativeGetFrontendInfo", "(I)Landroid/media/tv/tuner/frontend/FrontendInfo;", (void *)android_media_tv_Tuner_get_frontend_info }, { "nativeOpenFilter", "(IIJ)Landroid/media/tv/tuner/filter/Filter;", (void *)android_media_tv_Tuner_open_filter }, { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/filter/TimeFilter;", (void *)android_media_tv_Tuner_open_time_filter }, { "nativeGetLnbIds", "()[I", (void *)android_media_tv_Tuner_get_lnb_ids }, { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;", (void *)android_media_tv_Tuner_open_lnb_by_handle }, { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;", (void *)android_media_tv_Tuner_open_lnb_by_name }, { "nativeOpenDescramblerByHandle", "(I)Landroid/media/tv/tuner/Descrambler;", (void *)android_media_tv_Tuner_open_descrambler }, { "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;", (void *)android_media_tv_Tuner_open_dvr_recorder }, { "nativeOpenDvrPlayback", "(J)Landroid/media/tv/tuner/dvr/DvrPlayback;", (void *)android_media_tv_Tuner_open_dvr_playback }, { "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;", (void *)android_media_tv_Tuner_get_demux_caps }, { "nativeOpenDemuxByhandle", "(I)I", (void *)android_media_tv_Tuner_open_demux }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner }, { "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend }, { "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux }, }; static const JNINativeMethod gFilterMethods[] = { { "nativeConfigureFilter", "(IILandroid/media/tv/tuner/filter/FilterConfiguration;)I", (void *)android_media_tv_Tuner_configure_filter }, { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id }, { "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_set_filter_data_source }, { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter }, { "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter }, { "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter }, { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter }, }; static const JNINativeMethod gTimeFilterMethods[] = { { "nativeSetTimestamp", "(J)I", (void *)android_media_tv_Tuner_time_filter_set_timestamp }, { "nativeClearTimestamp", "()I", (void *)android_media_tv_Tuner_time_filter_clear_timestamp }, { "nativeGetTimestamp", "()Ljava/lang/Long;", (void *)android_media_tv_Tuner_time_filter_get_timestamp }, { "nativeGetSourceTime", "()Ljava/lang/Long;", (void *)android_media_tv_Tuner_time_filter_get_source_time }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_time_filter_close }, }; static const JNINativeMethod gDescramblerMethods[] = { { "nativeAddPid", "(IILandroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_descrambler_add_pid }, { "nativeRemovePid", "(IILandroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_descrambler_remove_pid }, { "nativeSetKeyToken", "([B)I", (void *)android_media_tv_Tuner_descrambler_set_key_token }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_descrambler }, }; static const JNINativeMethod gDvrRecorderMethods[] = { { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_attach_filter }, { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_detach_filter }, { "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I", (void *)android_media_tv_Tuner_configure_dvr }, { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr }, { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr }, { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr }, { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd }, { "nativeWrite", "(J)J", (void *)android_media_tv_Tuner_write_dvr }, { "nativeWrite", "([BJJ)J", (void *)android_media_tv_Tuner_write_dvr_to_array }, }; static const JNINativeMethod gDvrPlaybackMethods[] = { { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_attach_filter }, { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_detach_filter }, { "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I", (void *)android_media_tv_Tuner_configure_dvr }, { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr }, { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr }, { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr }, { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd }, { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr }, { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array }, }; static const JNINativeMethod gLnbMethods[] = { { "nativeSetVoltage", "(I)I", (void *)android_media_tv_Tuner_lnb_set_voltage }, { "nativeSetTone", "(I)I", (void *)android_media_tv_Tuner_lnb_set_tone }, { "nativeSetSatellitePosition", "(I)I", (void *)android_media_tv_Tuner_lnb_set_position }, { "nativeSendDiseqcMessage", "([B)I", (void *)android_media_tv_Tuner_lnb_send_diseqc_msg }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_lnb }, }; static const JNINativeMethod gMediaEventMethods[] = { { "nativeGetLinearBlock", "()Landroid/media/MediaCodec$LinearBlock;", (void *)android_media_tv_Tuner_media_event_get_linear_block }, { "nativeGetAudioHandle", "()Ljava/lang/Long;", (void *)android_media_tv_Tuner_media_event_get_audio_handle }, { "nativeFinalize", "()V", (void *)android_media_tv_Tuner_media_event_finalize }, }; static bool register_android_media_tv_Tuner(JNIEnv *env) { if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/Tuner", gTunerMethods, NELEM(gTunerMethods)) != JNI_OK) { ALOGE("Failed to register tuner native methods"); return false; } if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/filter/Filter", gFilterMethods, NELEM(gFilterMethods)) != JNI_OK) { ALOGE("Failed to register filter native methods"); return false; } if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/filter/TimeFilter", gTimeFilterMethods, NELEM(gTimeFilterMethods)) != JNI_OK) { ALOGE("Failed to register time filter native methods"); return false; } if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/Descrambler", gDescramblerMethods, NELEM(gDescramblerMethods)) != JNI_OK) { ALOGE("Failed to register descrambler native methods"); return false; } if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/dvr/DvrRecorder", gDvrRecorderMethods, NELEM(gDvrRecorderMethods)) != JNI_OK) { ALOGE("Failed to register dvr recorder native methods"); return false; } if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/dvr/DvrPlayback", gDvrPlaybackMethods, NELEM(gDvrPlaybackMethods)) != JNI_OK) { ALOGE("Failed to register dvr playback native methods"); return false; } if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/Lnb", gLnbMethods, NELEM(gLnbMethods)) != JNI_OK) { ALOGE("Failed to register lnb native methods"); return false; } if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/filter/MediaEvent", gMediaEventMethods, NELEM(gMediaEventMethods)) != JNI_OK) { ALOGE("Failed to register MediaEvent native methods"); return false; } return true; } jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { ALOGE("ERROR: GetEnv failed\n"); return result; } assert(env != NULL); if (!register_android_media_tv_Tuner(env)) { ALOGE("ERROR: Tuner native registration failed\n"); return result; } return JNI_VERSION_1_4; }