/* * 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_NDEBUG 0 #define LOG_TAG "TvTuner-JNI" #include "android_media_tv_Tuner.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "android_media_AudioPresentation.h" #include "android_media_MediaCodecLinearBlock.h" #include "android_runtime/AndroidRuntime.h" #pragma GCC diagnostic ignored "-Wunused-function" using ::aidl::android::hardware::tv::tuner::AudioExtraMetaData; using ::aidl::android::hardware::tv::tuner::AudioPreselection; using ::aidl::android::hardware::tv::tuner::AudioStreamType; using ::aidl::android::hardware::tv::tuner::AvStreamType; using ::aidl::android::hardware::tv::tuner::Constant; using ::aidl::android::hardware::tv::tuner::Constant64Bit; using ::aidl::android::hardware::tv::tuner::DataFormat; using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterSettingsFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterType; using ::aidl::android::hardware::tv::tuner::DemuxAlpLengthType; using ::aidl::android::hardware::tv::tuner::DemuxCapabilities; using ::aidl::android::hardware::tv::tuner::DemuxFilterAvSettings; using ::aidl::android::hardware::tv::tuner::DemuxFilterDownloadEvent; using ::aidl::android::hardware::tv::tuner::DemuxFilterDownloadSettings; using ::aidl::android::hardware::tv::tuner::DemuxFilterIpPayloadEvent; using ::aidl::android::hardware::tv::tuner::DemuxFilterMainType; using ::aidl::android::hardware::tv::tuner::DemuxFilterMediaEvent; using ::aidl::android::hardware::tv::tuner::DemuxFilterMediaEventExtraMetaData; using ::aidl::android::hardware::tv::tuner::DemuxFilterMmtpRecordEvent; using ::aidl::android::hardware::tv::tuner::DemuxFilterPesDataSettings; using ::aidl::android::hardware::tv::tuner::DemuxFilterPesEvent; using ::aidl::android::hardware::tv::tuner::DemuxFilterRecordSettings; using ::aidl::android::hardware::tv::tuner::DemuxFilterScIndexMask; using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionBits; using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionEvent; using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionSettings; using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionSettingsCondition; using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionSettingsConditionTableInfo; using ::aidl::android::hardware::tv::tuner::DemuxFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxFilterSubType; using ::aidl::android::hardware::tv::tuner::DemuxFilterTemiEvent; using ::aidl::android::hardware::tv::tuner::DemuxFilterTsRecordEvent; using ::aidl::android::hardware::tv::tuner::DemuxInfo; using ::aidl::android::hardware::tv::tuner::DemuxIpAddress; using ::aidl::android::hardware::tv::tuner::DemuxIpAddressIpAddress; using ::aidl::android::hardware::tv::tuner::DemuxIpFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxIpFilterSettingsFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxIpFilterType; using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterSettingsFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterType; using ::aidl::android::hardware::tv::tuner::DemuxQueueNotifyBits; using ::aidl::android::hardware::tv::tuner::DemuxRecordScIndexType; using ::aidl::android::hardware::tv::tuner::DemuxScHevcIndex; using ::aidl::android::hardware::tv::tuner::DemuxScIndex; using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterSettingsFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterType; using ::aidl::android::hardware::tv::tuner::DemuxTsFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxTsFilterSettingsFilterSettings; using ::aidl::android::hardware::tv::tuner::DemuxTsFilterType; using ::aidl::android::hardware::tv::tuner::DemuxTsIndex; using ::aidl::android::hardware::tv::tuner::DvrSettings; using ::aidl::android::hardware::tv::tuner::FilterDelayHint; using ::aidl::android::hardware::tv::tuner::FilterDelayHintType; using ::aidl::android::hardware::tv::tuner::FrontendAnalogAftFlag; using ::aidl::android::hardware::tv::tuner::FrontendAnalogSettings; using ::aidl::android::hardware::tv::tuner::FrontendAnalogSifStandard; using ::aidl::android::hardware::tv::tuner::FrontendAnalogType; using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Bandwidth; using ::aidl::android::hardware::tv::tuner::FrontendAtsc3CodeRate; using ::aidl::android::hardware::tv::tuner::FrontendAtsc3DemodOutputFormat; using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Fec; using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Modulation; using ::aidl::android::hardware::tv::tuner::FrontendAtsc3PlpSettings; using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Settings; using ::aidl::android::hardware::tv::tuner::FrontendAtsc3TimeInterleaveMode; using ::aidl::android::hardware::tv::tuner::FrontendAtscModulation; using ::aidl::android::hardware::tv::tuner::FrontendAtscSettings; using ::aidl::android::hardware::tv::tuner::FrontendBandwidth; using ::aidl::android::hardware::tv::tuner::FrontendCableTimeInterleaveMode; using ::aidl::android::hardware::tv::tuner::FrontendDtmbBandwidth; using ::aidl::android::hardware::tv::tuner::FrontendDtmbCapabilities; using ::aidl::android::hardware::tv::tuner::FrontendDtmbCodeRate; using ::aidl::android::hardware::tv::tuner::FrontendDtmbGuardInterval; using ::aidl::android::hardware::tv::tuner::FrontendDtmbModulation; using ::aidl::android::hardware::tv::tuner::FrontendDtmbSettings; using ::aidl::android::hardware::tv::tuner::FrontendDtmbTimeInterleaveMode; using ::aidl::android::hardware::tv::tuner::FrontendDtmbTransmissionMode; using ::aidl::android::hardware::tv::tuner::FrontendDvbcAnnex; using ::aidl::android::hardware::tv::tuner::FrontendDvbcBandwidth; using ::aidl::android::hardware::tv::tuner::FrontendDvbcModulation; using ::aidl::android::hardware::tv::tuner::FrontendDvbcOuterFec; using ::aidl::android::hardware::tv::tuner::FrontendDvbcSettings; using ::aidl::android::hardware::tv::tuner::FrontendDvbsCodeRate; using ::aidl::android::hardware::tv::tuner::FrontendDvbsModulation; using ::aidl::android::hardware::tv::tuner::FrontendDvbsPilot; using ::aidl::android::hardware::tv::tuner::FrontendDvbsRolloff; using ::aidl::android::hardware::tv::tuner::FrontendDvbsScanType; using ::aidl::android::hardware::tv::tuner::FrontendDvbsSettings; using ::aidl::android::hardware::tv::tuner::FrontendDvbsStandard; using ::aidl::android::hardware::tv::tuner::FrontendDvbsVcmMode; using ::aidl::android::hardware::tv::tuner::FrontendDvbtBandwidth; using ::aidl::android::hardware::tv::tuner::FrontendDvbtCoderate; using ::aidl::android::hardware::tv::tuner::FrontendDvbtConstellation; using ::aidl::android::hardware::tv::tuner::FrontendDvbtGuardInterval; using ::aidl::android::hardware::tv::tuner::FrontendDvbtHierarchy; using ::aidl::android::hardware::tv::tuner::FrontendDvbtPlpMode; using ::aidl::android::hardware::tv::tuner::FrontendDvbtSettings; using ::aidl::android::hardware::tv::tuner::FrontendDvbtStandard; using ::aidl::android::hardware::tv::tuner::FrontendDvbtTransmissionMode; using ::aidl::android::hardware::tv::tuner::FrontendGuardInterval; using ::aidl::android::hardware::tv::tuner::FrontendInnerFec; using ::aidl::android::hardware::tv::tuner::FrontendInterleaveMode; using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Coderate; using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Modulation; using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Rolloff; using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Settings; using ::aidl::android::hardware::tv::tuner::FrontendIsdbsCoderate; using ::aidl::android::hardware::tv::tuner::FrontendIsdbsModulation; using ::aidl::android::hardware::tv::tuner::FrontendIsdbsRolloff; using ::aidl::android::hardware::tv::tuner::FrontendIsdbsSettings; using ::aidl::android::hardware::tv::tuner::FrontendIsdbsStreamIdType; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtBandwidth; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtCoderate; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtGuardInterval; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtLayerSettings; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtMode; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtModulation; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtPartialReceptionFlag; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtSettings; using ::aidl::android::hardware::tv::tuner::FrontendIsdbtTimeInterleaveMode; using ::aidl::android::hardware::tv::tuner::FrontendIptvSettings; using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsFec; using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsProtocol; using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsIgmp; using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsFecType; using ::aidl::android::hardware::tv::tuner::FrontendModulation; using ::aidl::android::hardware::tv::tuner::FrontendModulationStatus; using ::aidl::android::hardware::tv::tuner::FrontendRollOff; using ::aidl::android::hardware::tv::tuner::FrontendScanAtsc3PlpInfo; using ::aidl::android::hardware::tv::tuner::FrontendScanMessageStandard; using ::aidl::android::hardware::tv::tuner::FrontendSpectralInversion; using ::aidl::android::hardware::tv::tuner::FrontendStatus; using ::aidl::android::hardware::tv::tuner::FrontendStatusAtsc3PlpInfo; using ::aidl::android::hardware::tv::tuner::FrontendStatusType; using ::aidl::android::hardware::tv::tuner::FrontendTransmissionMode; using ::aidl::android::hardware::tv::tuner::FrontendType; using ::aidl::android::hardware::tv::tuner::LnbPosition; using ::aidl::android::hardware::tv::tuner::LnbTone; using ::aidl::android::hardware::tv::tuner::LnbVoltage; using ::aidl::android::hardware::tv::tuner::PlaybackSettings; using ::aidl::android::hardware::tv::tuner::RecordSettings; using ::aidl::android::hardware::tv::tuner::VideoStreamType; struct fields_t { jfieldID tunerContext; jfieldID lnbContext; jfieldID filterContext; jfieldID timeFilterContext; jfieldID descramblerContext; jfieldID dvrRecorderContext; jfieldID dvrPlaybackContext; jfieldID mediaEventContext; jfieldID sharedFilterContext; 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; jmethodID sharedFilterInitID; jmethodID onSharedFilterStatusID; jmethodID onSharedFilterEventID; }; 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; android::Mutex::Autolock autoLock(event->mLock); if (event->mLinearBlockObj != nullptr) { JNIEnv *env = android::AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(event->mLinearBlockObj); event->mLinearBlockObj = nullptr; } event->mAvHandleRefCnt--; event->finalize(); event->decStrong(buf); } namespace android { static JAudioPresentationInfo::fields_t gAudioPresentationFields; /////////////// LnbClientCallbackImpl /////////////////////// void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) { ALOGV("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); ScopedLocalRef lnb(env, env->NewLocalRef(mLnbObj)); if (!env->IsSameObject(lnb.get(), nullptr)) { env->CallVoidMethod( lnb.get(), gFields.onLnbEventID, (jint)lnbEventType); } else { ALOGE("LnbClientCallbackImpl::onEvent:" "Lnb object has been freed. Ignoring callback."); } } void LnbClientCallbackImpl::onDiseqcMessage(const vector &diseqcMessage) { ALOGV("LnbClientCallbackImpl::onDiseqcMessage"); JNIEnv *env = AndroidRuntime::getJNIEnv(); ScopedLocalRef lnb(env, env->NewLocalRef(mLnbObj)); if (!env->IsSameObject(lnb.get(), nullptr)) { ScopedLocalRef array(env, env->NewByteArray(diseqcMessage.size())); env->SetByteArrayRegion(array.get(), 0, diseqcMessage.size(), reinterpret_cast(&diseqcMessage[0])); env->CallVoidMethod( lnb.get(), gFields.onLnbDiseqcMessageID, array.get()); } else { ALOGE("LnbClientCallbackImpl::onDiseqcMessage:" "Lnb object has been freed. Ignoring callback."); } } void LnbClientCallbackImpl::setLnb(jweak lnbObj) { ALOGV("LnbClientCallbackImpl::setLnb"); mLnbObj = lnbObj; } LnbClientCallbackImpl::~LnbClientCallbackImpl() { JNIEnv *env = AndroidRuntime::getJNIEnv(); if (mLnbObj != nullptr) { env->DeleteWeakGlobalRef(mLnbObj); mLnbObj = nullptr; } } /////////////// DvrClientCallbackImpl /////////////////////// void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) { ALOGV("DvrClientCallbackImpl::onRecordStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); ScopedLocalRef dvr(env, env->NewLocalRef(mDvrObj)); if (!env->IsSameObject(dvr.get(), nullptr)) { env->CallVoidMethod(dvr.get(), gFields.onDvrRecordStatusID, (jint)status); } else { ALOGE("DvrClientCallbackImpl::onRecordStatus:" "Dvr object has been freed. Ignoring callback."); } } void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) { ALOGV("DvrClientCallbackImpl::onPlaybackStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); ScopedLocalRef dvr(env, env->NewLocalRef(mDvrObj)); if (!env->IsSameObject(dvr.get(), nullptr)) { env->CallVoidMethod(dvr.get(), gFields.onDvrPlaybackStatusID, (jint)status); } else { ALOGE("DvrClientCallbackImpl::onPlaybackStatus:" "Dvr object has been freed. Ignoring callback."); } } void DvrClientCallbackImpl::setDvr(jweak dvrObj) { ALOGV("DvrClientCallbackImpl::setDvr"); mDvrObj = dvrObj; } DvrClientCallbackImpl::~DvrClientCallbackImpl() { JNIEnv *env = AndroidRuntime::getJNIEnv(); if (mDvrObj != nullptr) { env->DeleteWeakGlobalRef(mDvrObj); mDvrObj = nullptr; } } /////////////// C2DataIdInfo /////////////////////// C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, index) { CHECK(isGlobal()); CHECK_EQ(C2Param::INFO, kind()); mInfo = StubInfo(value); memcpy(static_cast(this) + 1, static_cast(&mInfo) + 1, kParamSize - sizeof(C2Param)); } /////////////// MediaEvent /////////////////////// MediaEvent::MediaEvent(sp filterClient, native_handle_t *avHandle, int64_t dataId, int64_t dataSize, jobject obj) : mFilterClient(filterClient), mDataId(dataId), mDataSize(dataSize), mBuffer(nullptr), mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) { JNIEnv *env = AndroidRuntime::getJNIEnv(); mMediaEventObj = env->NewWeakGlobalRef(obj); mAvHandle = avHandle; mLinearBlockObj = nullptr; } MediaEvent::~MediaEvent() { android::Mutex::Autolock autoLock(mLock); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mMediaEventObj); mMediaEventObj = nullptr; native_handle_delete(mAvHandle); if (mIonHandle != nullptr) { delete mIonHandle; } std::shared_ptr pC2Buffer = mC2Buffer.lock(); if (pC2Buffer != nullptr) { pC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this); } if (mLinearBlockObj != nullptr) { env->DeleteWeakGlobalRef(mLinearBlockObj); mLinearBlockObj = nullptr; } mFilterClient = nullptr; } void MediaEvent::finalize() { if (mAvHandleRefCnt == 0) { if (mFilterClient != nullptr) { mFilterClient->releaseAvHandle( mAvHandle, mDataIdRefCnt == 0 ? mDataId : 0); } native_handle_close(mAvHandle); } } jobject MediaEvent::getLinearBlock() { ALOGV("MediaEvent::getLinearBlock"); if (mAvHandle == nullptr) { return nullptr; } if (mLinearBlockObj != nullptr) { return mLinearBlockObj; } int fd; int numInts = 0; int memIndex; int dataSize; SharedHandleInfo info = mFilterClient->getAvSharedHandleInfo(); native_handle_t* avSharedHandle = info.sharedHandle; uint64_t avSharedMemSize = info.size; if (mAvHandle->numFds == 0) { if (avSharedHandle == nullptr) { ALOGE("Shared AV memory handle is not initialized."); return nullptr; } if (avSharedHandle->numFds == 0) { ALOGE("Shared AV memory handle is empty."); return nullptr; } fd = avSharedHandle->data[0]; dataSize = avSharedMemSize; numInts = avSharedHandle->numInts; if (numInts > 0) { // If the first int in the shared native handle has value, use it as the index memIndex = avSharedHandle->data[avSharedHandle->numFds]; } } else { fd = mAvHandle->data[0]; dataSize = mDataSize; numInts = mAvHandle->numInts; if (numInts > 0) { // Otherwise if the first int in the av native handle returned from the filter // event has value, use it as the index memIndex = mAvHandle->data[mAvHandle->numFds]; } else { if (avSharedHandle != nullptr) { numInts = avSharedHandle->numInts; if (numInts > 0) { // If the first int in the shared native handle has value, use it as the index memIndex = avSharedHandle->data[avSharedHandle->numFds]; } } } } mIonHandle = new C2HandleIon(dup(fd), dataSize); std::shared_ptr block = _C2BlockFactory::CreateLinearBlock(mIonHandle); if (block != nullptr) { // CreateLinearBlock delete mIonHandle after it create block successfully. // ToDo: coordinate who is response to delete mIonHandle mIonHandle = nullptr; JNIEnv *env = AndroidRuntime::getJNIEnv(); std::unique_ptr context{new JMediaCodecLinearBlock}; context->mBlock = block; std::shared_ptr pC2Buffer = context->toC2Buffer(0, dataSize); context->mBuffer = pC2Buffer; mC2Buffer = pC2Buffer; if (numInts > 0) { std::shared_ptr c2param = std::make_shared(memIndex, mDataId); std::shared_ptr info(std::static_pointer_cast(c2param)); pC2Buffer->setInfo(info); } pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this); incStrong(pC2Buffer.get()); 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 linearBlock; } else { native_handle_close(const_cast( reinterpret_cast(mIonHandle))); native_handle_delete(const_cast( reinterpret_cast(mIonHandle))); mIonHandle = nullptr; return nullptr; } } int64_t MediaEvent::getAudioHandle() { mDataIdRefCnt++; return mDataId; } /////////////// FilterClientCallbackImpl /////////////////////// void FilterClientCallbackImpl::getSectionEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterSectionEvent §ionEvent = event.get(); jint tableId = sectionEvent.tableId; jint version = sectionEvent.version; jint sectionNum = sectionEvent.sectionNum; jlong dataLength = sectionEvent.dataLength; ScopedLocalRef obj(env, env->NewObject(mSectionEventClass, mSectionEventInitID, tableId, version, sectionNum, dataLength)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getMediaEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterMediaEvent &mediaEvent = event.get(); ScopedLocalRef audioDescriptor(env); ScopedLocalRef presentationsJObj(env, JAudioPresentationInfo::asJobject( env, gAudioPresentationFields)); switch (mediaEvent.extraMetaData.getTag()) { case DemuxFilterMediaEventExtraMetaData::Tag::audio: { const AudioExtraMetaData &ad = mediaEvent.extraMetaData.get(); jbyte adFade = ad.adFade; jbyte adPan = ad.adPan; jchar versionTextTag = ad.versionTextTag; jbyte adGainCenter = ad.adGainCenter; jbyte adGainFront = ad.adGainFront; jbyte adGainSurround = ad.adGainSurround; audioDescriptor.reset(env->NewObject(mAudioDescriptorClass, mAudioDescriptorInitID, adFade, adPan, versionTextTag, adGainCenter, adGainFront, adGainSurround)); break; } case DemuxFilterMediaEventExtraMetaData::Tag::audioPresentations: { JAudioPresentationInfo::addPresentations( env, gAudioPresentationFields, mediaEvent.extraMetaData .get(), presentationsJObj.get()); break; } default: { ALOGE("FilterClientCallbackImpl::getMediaEvent: unknown extraMetaData"); break; } } jlong dataLength = mediaEvent.dataLength; jint streamId = mediaEvent.streamId; jboolean isPtsPresent = mediaEvent.isPtsPresent; jlong pts = mediaEvent.pts; jboolean isDtsPresent = mediaEvent.isDtsPresent; jlong dts = mediaEvent.dts; jlong offset = mediaEvent.offset; jboolean isSecureMemory = mediaEvent.isSecureMemory; jlong avDataId = mediaEvent.avDataId; jint mpuSequenceNumber = mediaEvent.mpuSequenceNumber; jboolean isPesPrivateData = mediaEvent.isPesPrivateData; jint sc = 0; if (mediaEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scIndex) { sc = mediaEvent.scIndexMask.get(); } else if (mediaEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scHevc) { sc = mediaEvent.scIndexMask.get(); } else if (mediaEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scAvc) { sc = mediaEvent.scIndexMask.get(); // Java uses the values defined by HIDL HAL. Left shift 4 bits. sc = sc << 4; } else if (mediaEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scVvc) { sc = mediaEvent.scIndexMask.get(); } ScopedLocalRef obj(env, env->NewObject(mMediaEventClass, mMediaEventInitID, streamId, isPtsPresent, pts, isDtsPresent, dts, dataLength, offset, nullptr, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData, sc, audioDescriptor.get(), presentationsJObj.get())); // Protect mFilterClient from being set to null. android::Mutex::Autolock autoLock(mLock); uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size; if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 || (dataLength > 0 && (dataLength + offset) < avSharedMemSize)) { sp mediaEventSp = new MediaEvent(mFilterClient, dupFromAidl(mediaEvent.avMemory), mediaEvent.avDataId, dataLength + offset, obj.get()); mediaEventSp->mAvHandleRefCnt++; env->SetLongField(obj.get(), mMediaEventFieldContextID, (jlong)mediaEventSp.get()); mediaEventSp->incStrong(obj.get()); } env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getPesEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterPesEvent &pesEvent = event.get(); jint streamId = pesEvent.streamId; jint dataLength = pesEvent.dataLength; jint mpuSequenceNumber = pesEvent.mpuSequenceNumber; ScopedLocalRef obj(env, env->NewObject(mPesEventClass, mPesEventInitID, streamId, dataLength, mpuSequenceNumber)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getTsRecordEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterTsRecordEvent &tsRecordEvent = event.get(); DemuxPid pid = tsRecordEvent.pid; jint jpid = static_cast(Constant::INVALID_TS_PID); if (pid.getTag() == DemuxPid::Tag::tPid) { jpid = pid.get(); } else if (pid.getTag() == DemuxPid::Tag::mmtpPid) { jpid = pid.get(); } jint sc = 0; if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scIndex) { sc = tsRecordEvent.scIndexMask.get(); } else if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scHevc) { sc = tsRecordEvent.scIndexMask.get(); } else if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scAvc) { sc = tsRecordEvent.scIndexMask.get(); // Java uses the values defined by HIDL HAL. Left shift 4 bits. sc = sc << 4; } else if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scVvc) { sc = tsRecordEvent.scIndexMask.get(); } jint ts = tsRecordEvent.tsIndexMask; jlong byteNumber = tsRecordEvent.byteNumber; jlong pts = tsRecordEvent.pts; jint firstMbInSlice = tsRecordEvent.firstMbInSlice; ScopedLocalRef obj(env, env->NewObject(mTsRecordEventClass, mTsRecordEventInitID, jpid, ts, sc, byteNumber, pts, firstMbInSlice)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getMmtpRecordEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterMmtpRecordEvent &mmtpRecordEvent = event.get(); jint scHevcIndexMask = mmtpRecordEvent.scHevcIndexMask; jlong byteNumber = mmtpRecordEvent.byteNumber; jint mpuSequenceNumber = mmtpRecordEvent.mpuSequenceNumber; jlong pts = mmtpRecordEvent.pts; jint firstMbInSlice = mmtpRecordEvent.firstMbInSlice; jlong tsIndexMask = mmtpRecordEvent.tsIndexMask; ScopedLocalRef obj(env, env->NewObject(mMmtpRecordEventClass, mMmtpRecordEventInitID, scHevcIndexMask, byteNumber, mpuSequenceNumber, pts, firstMbInSlice, tsIndexMask)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getDownloadEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterDownloadEvent &downloadEvent = event.get(); jint itemId = downloadEvent.itemId; jint downloadId = downloadEvent.downloadId; jint mpuSequenceNumber = downloadEvent.mpuSequenceNumber; jint itemFragmentIndex = downloadEvent.itemFragmentIndex; jint lastItemFragmentIndex = downloadEvent.lastItemFragmentIndex; jint dataLength = downloadEvent.dataLength; ScopedLocalRef obj(env, env->NewObject(mDownloadEventClass, mDownloadEventInitID, itemId, downloadId, mpuSequenceNumber, itemFragmentIndex, lastItemFragmentIndex, dataLength)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getIpPayloadEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterIpPayloadEvent &ipPayloadEvent = event.get(); jint dataLength = ipPayloadEvent.dataLength; ScopedLocalRef obj(env, env->NewObject(mIpPayloadEventClass, mIpPayloadEventInitID, dataLength)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getTemiEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterTemiEvent &temiEvent = event.get(); jlong pts = temiEvent.pts; jbyte descrTag = temiEvent.descrTag; std::vector descrData = temiEvent.descrData; ScopedLocalRef array(env, env->NewByteArray(descrData.size())); env->SetByteArrayRegion(array.get(), 0, descrData.size(), reinterpret_cast(&descrData[0])); ScopedLocalRef obj(env, env->NewObject(mTemiEventClass, mTemiEventInitID, pts, descrTag, array.get())); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getScramblingStatusEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterMonitorEvent &scramblingStatus = event.get() .get(); ScopedLocalRef obj(env, env->NewObject(mScramblingStatusEventClass, mScramblingStatusEventInitID, scramblingStatus)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getIpCidChangeEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const DemuxFilterMonitorEvent &cid = event.get() .get(); ScopedLocalRef obj(env, env->NewObject(mIpCidChangeEventClass, mIpCidChangeEventInitID, cid)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::getRestartEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent &event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); const int32_t &startId = event.get(); ScopedLocalRef obj(env, env->NewObject(mRestartEventClass, mRestartEventInitID, startId)); env->SetObjectArrayElement(arr, size, obj.get()); } void FilterClientCallbackImpl::onFilterEvent(const vector &events) { ALOGV("FilterClientCallbackImpl::onFilterEvent"); JNIEnv *env = AndroidRuntime::getJNIEnv(); ScopedLocalRef array(env); if (!events.empty()) { array.reset(env->NewObjectArray(events.size(), mEventClass, nullptr)); } for (int i = 0, arraySize = 0; i < events.size(); i++) { const DemuxFilterEvent &event = events[i]; switch (event.getTag()) { case DemuxFilterEvent::Tag::media: { getMediaEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterEvent::Tag::section: { getSectionEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterEvent::Tag::pes: { getPesEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterEvent::Tag::tsRecord: { getTsRecordEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterEvent::Tag::mmtpRecord: { getMmtpRecordEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterEvent::Tag::download: { getDownloadEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterEvent::Tag::ipPayload: { getIpPayloadEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterEvent::Tag::temi: { getTemiEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterEvent::Tag::monitorEvent: { switch (event.get().getTag()) { case DemuxFilterMonitorEvent::Tag::scramblingStatus: { getScramblingStatusEvent(array.get(), arraySize, event); arraySize++; break; } case DemuxFilterMonitorEvent::Tag::cid: { getIpCidChangeEvent(array.get(), arraySize, event); arraySize++; break; } default: { ALOGE("FilterClientCallbackImpl::onFilterEvent: unknown MonitorEvent"); break; } } break; } case DemuxFilterEvent::Tag::startId: { getRestartEvent(array.get(), arraySize, event); arraySize++; break; } default: { ALOGE("FilterClientCallbackImpl::onFilterEvent: unknown DemuxFilterEvent"); break; } } } ScopedLocalRef filter(env); { android::Mutex::Autolock autoLock(mLock); if (env->IsSameObject(mFilterObj, nullptr)) { ALOGE("FilterClientCallbackImpl::onFilterEvent:" "Filter object has been freed. Ignoring callback."); return; } else { filter.reset(env->NewLocalRef(mFilterObj)); } } jmethodID methodID = gFields.onFilterEventID; if (mSharedFilter) { methodID = gFields.onSharedFilterEventID; } env->CallVoidMethod(filter.get(), methodID, array.get()); } void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) { ALOGV("FilterClientCallbackImpl::onFilterStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); ScopedLocalRef filter(env); { android::Mutex::Autolock autoLock(mLock); if (env->IsSameObject(mFilterObj, nullptr)) { ALOGE("FilterClientCallbackImpl::onFilterStatus:" "Filter object has been freed. Ignoring callback."); return; } else { filter.reset(env->NewLocalRef(mFilterObj)); } } jmethodID methodID = gFields.onFilterStatusID; if (mSharedFilter) { methodID = gFields.onSharedFilterStatusID; } env->CallVoidMethod(filter.get(), methodID, (jint)static_cast(status)); } void FilterClientCallbackImpl::setFilter(jweak filterObj, sp filterClient) { ALOGV("FilterClientCallbackImpl::setFilter"); // Java Object android::Mutex::Autolock autoLock(mLock); mFilterObj = filterObj; mFilterClient = filterClient; mSharedFilter = false; } void FilterClientCallbackImpl::setSharedFilter(jweak filterObj, sp filterClient) { ALOGV("FilterClientCallbackImpl::setFilter"); // Java Object android::Mutex::Autolock autoLock(mLock); mFilterObj = filterObj; mFilterClient = filterClient; mSharedFilter = true; } FilterClientCallbackImpl::FilterClientCallbackImpl() { JNIEnv *env = AndroidRuntime::getJNIEnv(); ScopedLocalRef eventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/FilterEvent")); ScopedLocalRef sectionEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/SectionEvent")); ScopedLocalRef mediaEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/MediaEvent")); ScopedLocalRef audioDescriptorClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/AudioDescriptor")); ScopedLocalRef pesEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/PesEvent")); ScopedLocalRef tsRecordEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/TsRecordEvent")); ScopedLocalRef mmtpRecordEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent")); ScopedLocalRef downloadEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/DownloadEvent")); ScopedLocalRef ipPayloadEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent")); ScopedLocalRef temiEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/TemiEvent")); ScopedLocalRef scramblingStatusEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent")); ScopedLocalRef ipCidChangeEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent")); ScopedLocalRef restartEventClass = ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/RestartEvent")); mEventClass = (jclass) env->NewGlobalRef(eventClass.get()); mSectionEventClass = (jclass) env->NewGlobalRef(sectionEventClass.get()); mMediaEventClass = (jclass) env->NewGlobalRef(mediaEventClass.get()); mAudioDescriptorClass = (jclass) env->NewGlobalRef(audioDescriptorClass.get()); mPesEventClass = (jclass) env->NewGlobalRef(pesEventClass.get()); mTsRecordEventClass = (jclass) env->NewGlobalRef(tsRecordEventClass.get()); mMmtpRecordEventClass = (jclass) env->NewGlobalRef(mmtpRecordEventClass.get()); mDownloadEventClass = (jclass) env->NewGlobalRef(downloadEventClass.get()); mIpPayloadEventClass = (jclass) env->NewGlobalRef(ipPayloadEventClass.get()); mTemiEventClass = (jclass) env->NewGlobalRef(temiEventClass.get()); mScramblingStatusEventClass = (jclass) env->NewGlobalRef(scramblingStatusEventClass.get()); mIpCidChangeEventClass = (jclass) env->NewGlobalRef(ipCidChangeEventClass.get()); mRestartEventClass = (jclass) env->NewGlobalRef(restartEventClass.get()); mSectionEventInitID = env->GetMethodID(mSectionEventClass, "", "(IIIJ)V"); mMediaEventInitID = env->GetMethodID( mMediaEventClass, "", "(IZJZJJJLandroid/media/MediaCodec$LinearBlock;" "ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;" "Ljava/util/List;)V"); mAudioDescriptorInitID = env->GetMethodID(mAudioDescriptorClass, "", "(BBCBBB)V"); mPesEventInitID = env->GetMethodID(mPesEventClass, "", "(III)V"); mTsRecordEventInitID = env->GetMethodID(mTsRecordEventClass, "", "(IIIJJI)V"); mMmtpRecordEventInitID = env->GetMethodID(mMmtpRecordEventClass, "", "(IJIJII)V"); mDownloadEventInitID = env->GetMethodID(mDownloadEventClass, "", "(IIIIII)V"); mIpPayloadEventInitID = env->GetMethodID(mIpPayloadEventClass, "", "(I)V"); mTemiEventInitID = env->GetMethodID(mTemiEventClass, "", "(JB[B)V"); mScramblingStatusEventInitID = env->GetMethodID(mScramblingStatusEventClass, "", "(I)V"); mIpCidChangeEventInitID = env->GetMethodID(mIpCidChangeEventClass, "", "(I)V"); mRestartEventInitID = env->GetMethodID(mRestartEventClass, "", "(I)V"); mMediaEventFieldContextID = env->GetFieldID(mMediaEventClass, "mNativeContext", "J"); } FilterClientCallbackImpl::~FilterClientCallbackImpl() { JNIEnv *env = AndroidRuntime::getJNIEnv(); { android::Mutex::Autolock autoLock(mLock); if (mFilterObj != nullptr) { env->DeleteWeakGlobalRef(mFilterObj); mFilterObj = nullptr; } mFilterClient = nullptr; } env->DeleteGlobalRef(mEventClass); env->DeleteGlobalRef(mSectionEventClass); env->DeleteGlobalRef(mMediaEventClass); env->DeleteGlobalRef(mAudioDescriptorClass); env->DeleteGlobalRef(mPesEventClass); env->DeleteGlobalRef(mTsRecordEventClass); env->DeleteGlobalRef(mMmtpRecordEventClass); env->DeleteGlobalRef(mDownloadEventClass); env->DeleteGlobalRef(mIpPayloadEventClass); env->DeleteGlobalRef(mTemiEventClass); env->DeleteGlobalRef(mScramblingStatusEventClass); env->DeleteGlobalRef(mIpCidChangeEventClass); env->DeleteGlobalRef(mRestartEventClass); } /////////////// FrontendClientCallbackImpl /////////////////////// FrontendClientCallbackImpl::FrontendClientCallbackImpl(JTuner* jtuner, jweak listener) { ALOGV("FrontendClientCallbackImpl() with listener:%p", listener); addCallbackListener(jtuner, listener); } void FrontendClientCallbackImpl::addCallbackListener(JTuner* jtuner, jweak listener) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jweak listenerRef = env->NewWeakGlobalRef(listener); ALOGV("addCallbackListener() with listener:%p and ref:%p @%p", listener, listenerRef, this); std::scoped_lock lock(mMutex); mListenersMap[jtuner] = listenerRef; } void FrontendClientCallbackImpl::removeCallbackListener(JTuner* listener) { ALOGV("removeCallbackListener for listener:%p", listener); JNIEnv *env = AndroidRuntime::getJNIEnv(); std::scoped_lock lock(mMutex); if (mListenersMap.find(listener) != mListenersMap.end() && mListenersMap[listener]) { env->DeleteWeakGlobalRef(mListenersMap[listener]); mListenersMap.erase(listener); } } void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) { ALOGV("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); std::scoped_lock lock(mMutex); for (const auto& mapEntry : mListenersMap) { ALOGV("JTuner:%p, jweak:%p", mapEntry.first, mapEntry.second); ScopedLocalRef frontend(env, env->NewLocalRef(mapEntry.second)); if (!env->IsSameObject(frontend.get(), nullptr)) { env->CallVoidMethod( frontend.get(), gFields.onFrontendEventID, (jint)frontendEventType); } else { ALOGW("FrontendClientCallbackImpl::onEvent:" "Frontend object has been freed. Ignoring callback."); } } } void FrontendClientCallbackImpl::onScanMessage( FrontendScanMessageType type, const FrontendScanMessage& message) { ALOGV("FrontendClientCallbackImpl::onScanMessage, type=%d", type); JNIEnv *env = AndroidRuntime::getJNIEnv(); ScopedLocalRef clazz(env, env->FindClass("android/media/tv/tuner/Tuner")); std::scoped_lock lock(mMutex); for (const auto& mapEntry : mListenersMap) { ScopedLocalRef frontend(env, env->NewLocalRef(mapEntry.second)); if (env->IsSameObject(frontend.get(), nullptr)) { ALOGE("FrontendClientCallbackImpl::onScanMessage:" "Tuner object has been freed. Ignoring callback."); continue; } executeOnScanMessage(env, clazz.get(), frontend.get(), type, message); } } void FrontendClientCallbackImpl::executeOnScanMessage( JNIEnv *env, const jclass& clazz, const jobject& frontend, FrontendScanMessageType type, const FrontendScanMessage& message) { ALOGV("FrontendClientCallbackImpl::executeOnScanMessage, type=%d", type); switch(type) { case FrontendScanMessageType::LOCKED: { if (message.get()) { env->CallVoidMethod( frontend, env->GetMethodID(clazz, "onLocked", "()V")); } else { env->CallVoidMethod( frontend, env->GetMethodID(clazz, "onUnlocked", "()V")); } break; } case FrontendScanMessageType::END: { if (message.get()) { env->CallVoidMethod( frontend, env->GetMethodID(clazz, "onScanStopped", "()V")); } break; } case FrontendScanMessageType::PROGRESS_PERCENT: { env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onProgress", "(I)V"), message.get()); break; } case FrontendScanMessageType::FREQUENCY: { std::vector v = message.get(); ScopedLocalRef freqs(env, env->NewLongArray(v.size())); env->SetLongArrayRegion(freqs.get(), 0, v.size(), reinterpret_cast(&v[0])); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onFrequenciesReport", "([J)V"), freqs.get()); break; } case FrontendScanMessageType::SYMBOL_RATE: { std::vector v = message.get(); ScopedLocalRef symbolRates(env, env->NewIntArray(v.size())); env->SetIntArrayRegion(symbolRates.get(), 0, v.size(), reinterpret_cast(&v[0])); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onSymbolRates", "([I)V"), symbolRates.get()); break; } case FrontendScanMessageType::HIERARCHY: { env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onHierarchy", "(I)V"), (jint)message.get()); break; } case FrontendScanMessageType::ANALOG_TYPE: { env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onSignalType", "(I)V"), (jint)message.get()); break; } case FrontendScanMessageType::PLP_IDS: { std::vector jintV = message.get(); ScopedLocalRef plpIds(env, env->NewIntArray(jintV.size())); env->SetIntArrayRegion(plpIds.get(), 0, jintV.size(), reinterpret_cast(&jintV[0])); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onPlpIds", "([I)V"), plpIds.get()); break; } case FrontendScanMessageType::GROUP_IDS: { std::vector jintV = message.get(); ScopedLocalRef groupIds(env, env->NewIntArray(jintV.size())); env->SetIntArrayRegion(groupIds.get(), 0, jintV.size(), reinterpret_cast(&jintV[0])); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onGroupIds", "([I)V"), groupIds.get()); break; } case FrontendScanMessageType::INPUT_STREAM_IDS: { std::vector jintV = message.get(); ScopedLocalRef streamIds(env, env->NewIntArray(jintV.size())); env->SetIntArrayRegion(streamIds.get(), 0, jintV.size(), reinterpret_cast(&jintV[0])); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onInputStreamIds", "([I)V"), streamIds.get()); break; } case FrontendScanMessageType::STANDARD: { FrontendScanMessageStandard std = message.get(); jint standard; if (std.getTag() == FrontendScanMessageStandard::Tag::sStd) { standard = (jint)std.get(); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onDvbsStandard", "(I)V"), standard); } else if (std.getTag() == FrontendScanMessageStandard::Tag::tStd) { standard = (jint)std.get(); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onDvbtStandard", "(I)V"), standard); } else if (std.getTag() == FrontendScanMessageStandard::Tag::sifStd) { standard = (jint)std.get(); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onAnalogSifStandard", "(I)V"), standard); } break; } case FrontendScanMessageType::ATSC3_PLP_INFO: { ScopedLocalRef plpClazz(env, env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo")); jmethodID init = env->GetMethodID(plpClazz.get(), "", "(IZ)V"); std::vector plpInfos = message.get(); ScopedLocalRef array(env, env->NewObjectArray(plpInfos.size(), plpClazz.get(), nullptr)); for (int i = 0; i < plpInfos.size(); i++) { const FrontendScanAtsc3PlpInfo &info = plpInfos[i]; jint plpId = info.plpId; jboolean lls = info.bLlsFlag; ScopedLocalRef obj(env, env->NewObject(plpClazz.get(), init, plpId, lls)); env->SetObjectArrayElement(array.get(), i, obj.get()); } env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onAtsc3PlpInfos", "([Landroid/media/tv/tuner/frontend/" "Atsc3PlpInfo;)V"), array.get()); break; } case FrontendScanMessageType::MODULATION: { jint modulationType = -1; FrontendModulation modulation = message.get(); switch (modulation.getTag()) { case FrontendModulation::Tag::dvbc: { modulationType = (jint)modulation.get(); break; } case FrontendModulation::Tag::dvbt: { modulationType = (jint)modulation.get(); break; } case FrontendModulation::Tag::dvbs: { modulationType = (jint)modulation.get(); break; } case FrontendModulation::Tag::isdbs: { modulationType = (jint)modulation.get(); break; } case FrontendModulation::Tag::isdbs3: { modulationType = (jint)modulation.get(); break; } case FrontendModulation::Tag::isdbt: { modulationType = (jint)modulation.get(); break; } case FrontendModulation::Tag::atsc: { modulationType = (jint)modulation.get(); break; } case FrontendModulation::Tag::atsc3: { modulationType = (jint)modulation.get(); break; } case FrontendModulation::Tag::dtmb: { modulationType = (jint)modulation.get(); break; } default: { break; } } if (modulationType > 0) { env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onModulationReported", "(I)V"), modulationType); } break; } case FrontendScanMessageType::HIGH_PRIORITY: { bool isHighPriority = message.get(); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onPriorityReported", "(Z)V"), isHighPriority); break; } case FrontendScanMessageType::DVBC_ANNEX: { jint dvbcAnnex = (jint)message.get(); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onDvbcAnnexReported", "(I)V"), dvbcAnnex); break; } case FrontendScanMessageType::DVBT_CELL_IDS: { std::vector jintV = message.get(); ScopedLocalRef cellIds(env, env->NewIntArray(jintV.size())); env->SetIntArrayRegion(cellIds.get(), 0, jintV.size(), reinterpret_cast(&jintV[0])); env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onDvbtCellIdsReported", "([I)V"), cellIds.get()); break; } default: break; } } FrontendClientCallbackImpl::~FrontendClientCallbackImpl() { JNIEnv *env = android::AndroidRuntime::getJNIEnv(); ALOGV("~FrontendClientCallbackImpl()"); std::scoped_lock lock(mMutex); for (const auto& mapEntry : mListenersMap) { ALOGV("deleteRef :%p at @ %p", mapEntry.second, this); env->DeleteWeakGlobalRef(mapEntry.second); } mListenersMap.clear(); } /////////////// Tuner /////////////////////// sp JTuner::sTunerClient = nullptr; std::mutex JTuner::sTunerClientMutex; JTuner::JTuner(JNIEnv *env, jobject thiz) : mClass(nullptr) { jclass clazz = env->GetObjectClass(thiz); CHECK(clazz != nullptr); mClass = (jclass)env->NewGlobalRef(clazz); mObject = env->NewWeakGlobalRef(thiz); { std::scoped_lock lock(sTunerClientMutex); if (sTunerClient == nullptr) { sTunerClient = new TunerClient(); } else { sTunerClient->incStrong(this); } ALOGV("JTuner refs count: %d", sTunerClient->getStrongCount()); } mSharedFeId = (int)Constant::INVALID_FRONTEND_ID; } jweak JTuner::getObject() { return mObject; } JTuner::~JTuner() { if (mFeClient != nullptr) { mFeClient->close(); } if (mDemuxClient != nullptr) { mDemuxClient->close(); } JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mObject); env->DeleteGlobalRef(mClass); mFeClient = nullptr; mFeClientCb = nullptr; mDemuxClient = nullptr; { std::scoped_lock lock(sTunerClientMutex); int32_t refCnt = sTunerClient->getStrongCount(); ALOGV("~JTuner refs count: %d", refCnt); if (refCnt == 1) { sTunerClient = nullptr; } else { sTunerClient->decStrong(this); } } mClass = nullptr; mObject = nullptr; } jint JTuner::getTunerVersion() { ALOGV("JTuner::getTunerVersion()"); return (jint)sTunerClient->getHalTunerVersion(); } jobject JTuner::getFrontendIds() { ALOGV("JTuner::getFrontendIds()"); vector ids = sTunerClient->getFrontendIds(); if (ids.size() == 0) { ALOGW("Frontend isn't available"); return nullptr; } 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 < ids.size(); i++) { jobject idObj = env->NewObject(integerClazz, intInit, ids[i]); env->CallBooleanMethod(obj, arrayListAdd, idObj); } return obj; } jobject JTuner::openFrontendByHandle(int feHandle) { // TODO: Handle reopening frontend with different handle sp feClient = sTunerClient->openFrontend(feHandle); if (feClient == nullptr) { ALOGE("Failed to open frontend"); return nullptr; } mFeClient = feClient; mFeId = mFeClient->getId(); if (mDemuxClient != nullptr) { mDemuxClient->setFrontendDataSource(mFeClient); } JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject tuner(env->NewLocalRef(mObject)); if (env->IsSameObject(tuner, nullptr)) { ALOGE("openFrontendByHandle" "Tuner object has been freed. Failed to open frontend."); return nullptr; } mFeClientCb = new FrontendClientCallbackImpl(this, mObject); mFeClient->setCallback(mFeClientCb); // TODO: add more fields to frontend return env->NewObject( env->FindClass("android/media/tv/tuner/Tuner$Frontend"), gFields.frontendInitID, tuner, (jint) mFeId); } int JTuner::shareFrontend(int feId) { if (mFeClient != nullptr) { ALOGE("Cannot share frontend:%d because this session is already holding %d", feId, mFeClient->getId()); return (int)Result::INVALID_STATE; } if (mDemuxClient != NULL) { mDemuxClient->setFrontendDataSourceById(feId); } mSharedFeId = feId; return (int)Result::SUCCESS; } int JTuner::unshareFrontend() { if (mFeClient != nullptr) { ALOGE("Cannot unshare frontend because this session is already holding %d" " as an owner instead of as a sharee", mFeClient->getId()); return (int)Result::INVALID_STATE; } mSharedFeId = (int)Constant::INVALID_FRONTEND_ID; return (int)Result::SUCCESS; } void JTuner::registerFeCbListener(JTuner* jtuner) { ALOGV("registerFeCbListener: %p", jtuner); if (mFeClientCb != nullptr && jtuner != nullptr) { mFeClientCb->addCallbackListener(jtuner, jtuner->getObject()); } } void JTuner::unregisterFeCbListener(JTuner* jtuner) { ALOGV("unregisterFeCbListener: %p", jtuner); if (mFeClientCb != nullptr && jtuner != nullptr) { mFeClientCb->removeCallbackListener(jtuner); } } void JTuner::updateFrontend(JTuner* jtuner) { if (jtuner == nullptr) { ALOGV("JTuner::updateFrontend(null) called for previous owner: %p", this); mFeClient = nullptr; mFeClientCb = nullptr; } else { ALOGV("JTuner::updateFrontend(%p) called for new owner: %p", jtuner, this); mFeClient = jtuner->mFeClient; mFeClientCb = jtuner->mFeClientCb; } } jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(II)V"); jint typeCap = caps.get().typeCap; jint sifStandardCap = caps.get().sifStandardCap; return env->NewObject(clazz, capsInit, typeCap, sifStandardCap); } jobject JTuner::getAtsc3FrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIIII)V"); jint bandwidthCap = caps.get().bandwidthCap; jint modulationCap = caps.get().modulationCap; jint timeInterleaveModeCap = caps.get().timeInterleaveModeCap; jint codeRateCap = caps.get().codeRateCap; jint fecCap = caps.get().fecCap; jint demodOutputFormatCap = caps.get().demodOutputFormatCap; return env->NewObject(clazz, capsInit, bandwidthCap, modulationCap, timeInterleaveModeCap, codeRateCap, fecCap, demodOutputFormatCap); } jobject JTuner::getAtscFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AtscFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(I)V"); jint modulationCap = caps.get().modulationCap; return env->NewObject(clazz, capsInit, modulationCap); } jobject JTuner::getDvbcFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IJI)V"); jint modulationCap = caps.get().modulationCap; jlong fecCap = caps.get().fecCap; jint annexCap = caps.get().annexCap; return env->NewObject(clazz, capsInit, modulationCap, fecCap, annexCap); } jobject JTuner::getDvbsFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IJI)V"); jint modulationCap = caps.get().modulationCap; jlong innerfecCap = caps.get().innerfecCap; jint standard = caps.get().standard; return env->NewObject(clazz, capsInit, modulationCap, innerfecCap, standard); } jobject JTuner::getDvbtFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIIIIZZ)V"); jint transmissionModeCap = caps.get().transmissionModeCap; jint bandwidthCap = caps.get().bandwidthCap; jint constellationCap = caps.get().constellationCap; jint coderateCap = caps.get().coderateCap; jint hierarchyCap = caps.get().hierarchyCap; jint guardIntervalCap = caps.get().guardIntervalCap; jboolean isT2Supported = caps.get().isT2Supported; jboolean isMisoSupported = caps.get().isMisoSupported; return env->NewObject(clazz, capsInit, transmissionModeCap, bandwidthCap, constellationCap, coderateCap, hierarchyCap, guardIntervalCap, isT2Supported, isMisoSupported); } jobject JTuner::getIsdbs3FrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(II)V"); jint modulationCap = caps.get().modulationCap; jint coderateCap = caps.get().coderateCap; return env->NewObject(clazz, capsInit, modulationCap, coderateCap); } jobject JTuner::getIsdbsFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(II)V"); jint modulationCap = caps.get().modulationCap; jint coderateCap = caps.get().coderateCap; return env->NewObject(clazz, capsInit, modulationCap, coderateCap); } jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIIIIZZ)V"); jint modeCap = caps.get().modeCap; jint bandwidthCap = caps.get().bandwidthCap; jint modulationCap = caps.get().modulationCap; jint coderateCap = caps.get().coderateCap; jint guardIntervalCap = caps.get().guardIntervalCap; jint timeInterleaveCap = caps.get().timeInterleaveCap; jboolean isSegmentAuto = caps.get().isSegmentAuto; jboolean isFullSegment = caps.get().isFullSegment; return env->NewObject(clazz, capsInit, modeCap, bandwidthCap, modulationCap, coderateCap, guardIntervalCap, timeInterleaveCap, isSegmentAuto, isFullSegment); } jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIIII)V"); jint modulationCap = caps.get().modulationCap; jint transmissionModeCap = caps.get().transmissionModeCap; jint guardIntervalCap = caps.get().guardIntervalCap; jint interleaveModeCap = caps.get().interleaveModeCap; jint codeRateCap = caps.get().codeRateCap; jint bandwidthCap = caps.get().bandwidthCap; return env->NewObject(clazz, capsInit, modulationCap, transmissionModeCap, guardIntervalCap, interleaveModeCap, codeRateCap, bandwidthCap); } jobject JTuner::getIptvFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(I)V"); jint protocolCap = caps.get()->protocolCap; return env->NewObject(clazz, capsInit, protocolCap); } jobject JTuner::getFrontendInfo(int id) { shared_ptr feInfo; feInfo = sTunerClient->getFrontendInfo(id); if (feInfo == nullptr) { return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendInfo"); jmethodID infoInit = env->GetMethodID(clazz, "", "(IIJJIIJI[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V"); jint type = (jint)feInfo->type; jlong minFrequency = feInfo->minFrequency; jlong maxFrequency = feInfo->maxFrequency; jint minSymbolRate = feInfo->minSymbolRate; jint maxSymbolRate = feInfo->maxSymbolRate; jlong 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])); FrontendCapabilities caps = feInfo->frontendCaps; jobject jcaps = nullptr; switch(feInfo->type) { case FrontendType::ANALOG: if (FrontendCapabilities::Tag::analogCaps == caps.getTag()) { jcaps = getAnalogFrontendCaps(env, caps); } break; case FrontendType::ATSC3: if (FrontendCapabilities::Tag::atsc3Caps == caps.getTag()) { jcaps = getAtsc3FrontendCaps(env, caps); } break; case FrontendType::ATSC: if (FrontendCapabilities::Tag::atscCaps == caps.getTag()) { jcaps = getAtscFrontendCaps(env, caps); } break; case FrontendType::DVBC: if (FrontendCapabilities::Tag::dvbcCaps == caps.getTag()) { jcaps = getDvbcFrontendCaps(env, caps); } break; case FrontendType::DVBS: if (FrontendCapabilities::Tag::dvbsCaps == caps.getTag()) { jcaps = getDvbsFrontendCaps(env, caps); } break; case FrontendType::DVBT: if (FrontendCapabilities::Tag::dvbtCaps == caps.getTag()) { jcaps = getDvbtFrontendCaps(env, caps); } break; case FrontendType::ISDBS: if (FrontendCapabilities::Tag::isdbsCaps == caps.getTag()) { jcaps = getIsdbsFrontendCaps(env, caps); } break; case FrontendType::ISDBS3: if (FrontendCapabilities::Tag::isdbs3Caps == caps.getTag()) { jcaps = getIsdbs3FrontendCaps(env, caps); } break; case FrontendType::ISDBT: if (FrontendCapabilities::Tag::isdbtCaps == caps.getTag()) { jcaps = getIsdbtFrontendCaps(env, caps); } break; case FrontendType::DTMB: if (FrontendCapabilities::Tag::dtmbCaps == caps.getTag()) { jcaps = getDtmbFrontendCaps(env, caps); } break; case FrontendType::IPTV: if (FrontendCapabilities::Tag::iptvCaps == caps.getTag()) { jcaps = getIptvFrontendCaps(env, caps); } break; default: break; } return env->NewObject(clazz, infoInit, id, type, minFrequency, maxFrequency, minSymbolRate, maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps); } Result JTuner::getFrontendHardwareInfo(string &info) { if (mFeClient == nullptr) { ALOGE("frontend is not initialized"); return Result::INVALID_STATE; } return mFeClient->getHardwareInfo(info); } jint JTuner::setMaxNumberOfFrontends(int32_t type, int32_t maxNumber) { if (sTunerClient == nullptr) { ALOGE("tuner is not initialized"); return (jint)Result::INVALID_STATE; } return (jint)sTunerClient->setMaxNumberOfFrontends(static_cast(type), maxNumber); } int32_t JTuner::getMaxNumberOfFrontends(int32_t type) { if (sTunerClient == nullptr) { ALOGE("tuner is not initialized"); return -1; } return sTunerClient->getMaxNumberOfFrontends(static_cast(type)); } jint JTuner::removeOutputPid(int32_t pid) { if (mFeClient == nullptr) { ALOGE("frontend is not initialized"); return (jint)Result::INVALID_STATE; } return (jint)mFeClient->removeOutputPid(pid); } jobjectArray JTuner::getFrontendStatusReadiness(jintArray types) { if (mFeClient == nullptr) { ALOGE("frontend is not initialized"); return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jsize size = env->GetArrayLength(types); jint intTypes[size]; env->GetIntArrayRegion(types, 0, size, intTypes); std::vector v; for (int i = 0; i < size; i++) { v.push_back(static_cast(intTypes[i])); } vector readiness = mFeClient->getStatusReadiness(v); if (readiness.size() < size) { return nullptr; } jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatusReadiness"); jmethodID init = env->GetMethodID(clazz, "", "(II)V"); jobjectArray valObj = env->NewObjectArray(size, clazz, nullptr); for (int i = 0; i < size; i++) { ScopedLocalRef readinessObj(env, env->NewObject(clazz, init, intTypes[i], readiness[i])); env->SetObjectArrayElement(valObj, i, readinessObj.get()); } return valObj; } jobject JTuner::openLnbByHandle(int handle) { if (sTunerClient == nullptr) { return nullptr; } sp lnbClient; sp callback = new LnbClientCallbackImpl(); lnbClient = sTunerClient->openLnb(handle); if (lnbClient == nullptr) { ALOGD("Failed to open lnb, handle = %d", handle); return nullptr; } if (lnbClient->setCallback(callback) != Result::SUCCESS) { ALOGD("Failed to set lnb callback"); return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject lnbObj = env->NewObject( env->FindClass("android/media/tv/tuner/Lnb"), gFields.lnbInitID); lnbClient->incStrong(lnbObj); env->SetLongField(lnbObj, gFields.lnbContext, (jlong)lnbClient.get()); callback->setLnb(env->NewWeakGlobalRef(lnbObj)); return lnbObj; } jobject JTuner::openLnbByName(jstring name) { if (sTunerClient == nullptr) { return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); std::string lnbName(env->GetStringUTFChars(name, nullptr)); sp lnbClient; sp callback = new LnbClientCallbackImpl(); lnbClient = sTunerClient->openLnbByName(lnbName); if (lnbClient == nullptr) { ALOGD("Failed to open lnb by name, name = %s", lnbName.c_str()); return nullptr; } if (lnbClient->setCallback(callback) != Result::SUCCESS) { ALOGD("Failed to set lnb callback"); return nullptr; } jobject lnbObj = env->NewObject( env->FindClass("android/media/tv/tuner/Lnb"), gFields.lnbInitID); lnbClient->incStrong(lnbObj); env->SetLongField(lnbObj, gFields.lnbContext, (jlong)lnbClient.get()); callback->setLnb(env->NewWeakGlobalRef(lnbObj)); return lnbObj; } int JTuner::tune(const FrontendSettings &settings) { if (mFeClient == nullptr) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } return (int)mFeClient->tune(settings); } int JTuner::stopTune() { if (mFeClient == nullptr) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } return (int) mFeClient->stopTune(); } int JTuner::scan(const FrontendSettings &settings, FrontendScanType scanType) { if (mFeClient == nullptr) { ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFeClient->scan(settings, scanType); return (int)result; } int JTuner::stopScan() { if (mFeClient == nullptr) { ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFeClient->stopScan(); return (int)result; } int JTuner::setLnb(sp lnbClient) { if (mFeClient == nullptr) { ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } if (lnbClient == nullptr) { ALOGE("lnb is not initialized"); return (int)Result::INVALID_STATE; } Result result = mFeClient->setLnb(lnbClient); return (int)result; } bool JTuner::isLnaSupported() { if (sTunerClient == nullptr) { return (int)Result::NOT_INITIALIZED; } return sTunerClient->isLnaSupported(); } int JTuner::setLna(bool enable) { if (sTunerClient == nullptr) { return (int)Result::NOT_INITIALIZED; } Result result = sTunerClient->setLna(enable); return (int)result; } Result JTuner::openDemux(int handle) { if (sTunerClient == nullptr) { return Result::NOT_INITIALIZED; } if (mDemuxClient == nullptr) { mDemuxClient = sTunerClient->openDemux(handle); if (mDemuxClient == nullptr) { ALOGE("Failed to open demux"); return Result::UNKNOWN_ERROR; } if (mFeClient != nullptr) { return mDemuxClient->setFrontendDataSource(mFeClient); } else if (mSharedFeId != (int)Constant::INVALID_FRONTEND_ID) { return mDemuxClient->setFrontendDataSourceById(mSharedFeId); } } return Result::SUCCESS; } jint JTuner::close() { Result res = Result::SUCCESS; if (mFeClient != nullptr) { res = mFeClient->close(); if (res != Result::SUCCESS) { return (jint)res; } mFeClient = nullptr; } if (mDemuxClient != nullptr) { res = mDemuxClient->close(); if (res != Result::SUCCESS) { return (jint)res; } mDemuxClient = nullptr; } mSharedFeId = (int)Constant::INVALID_FRONTEND_ID; return (jint)res; } jobject JTuner::getAvSyncHwId(sp filterClient) { if (mDemuxClient == nullptr) { return nullptr; } int avSyncHwId = mDemuxClient->getAvSyncHwId(filterClient); if (avSyncHwId >= 0) { 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 nullptr; } jobject JTuner::getAvSyncTime(jint id) { if (mDemuxClient == nullptr) { return nullptr; } int64_t time = mDemuxClient->getAvSyncTime((int)id); if (time >= 0) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); return env->NewObject(longClazz, longInit, time); } return nullptr; } int JTuner::connectCiCam(jint id) { if (mDemuxClient == nullptr) { return (int)Result::NOT_INITIALIZED; } return (int)mDemuxClient->connectCiCam((int)id); } int JTuner::linkCiCam(int id) { if (mFeClient == nullptr) { ALOGE("frontend client is not initialized"); return (int)Constant::INVALID_LTS_ID; } return mFeClient->linkCiCamToFrontend(id); } int JTuner::disconnectCiCam() { if (mDemuxClient == nullptr) { return (int)Result::NOT_INITIALIZED; } return (int)mDemuxClient->disconnectCiCam(); } int JTuner::unlinkCiCam(int id) { if (mFeClient == nullptr) { ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } return (int)mFeClient->unlinkCiCamToFrontend(id); } jobject JTuner::openDescrambler() { ALOGV("JTuner::openDescrambler"); if (sTunerClient == nullptr || mDemuxClient == nullptr) { return nullptr; } sp descramblerClient = sTunerClient->openDescrambler(0 /*unused*/); if (descramblerClient == nullptr) { ALOGD("Failed to open descrambler"); return nullptr; } descramblerClient->setDemuxSource(mDemuxClient); JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject descramblerObj = env->NewObject( env->FindClass("android/media/tv/tuner/Descrambler"), gFields.descramblerInitID); descramblerClient->incStrong(descramblerObj); env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerClient.get()); return descramblerObj; } jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { if (mDemuxClient == nullptr) { return nullptr; } sp filterClient; sp callback = new FilterClientCallbackImpl(); filterClient = mDemuxClient->openFilter(type, bufferSize, callback); if (filterClient == nullptr) { ALOGD("Failed to open filter, type = %d", type.mainType); return nullptr; } int64_t fId; Result res = filterClient->getId64Bit(fId); if (res != Result::SUCCESS) { int32_t id; filterClient->getId(id); fId = static_cast(id); } JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject filterObj = env->NewObject(env->FindClass("android/media/tv/tuner/filter/Filter"), gFields.filterInitID, fId); filterClient->incStrong(filterObj); env->SetLongField(filterObj, gFields.filterContext, (jlong)filterClient.get()); callback->setFilter(env->NewWeakGlobalRef(filterObj), filterClient); return filterObj; } jobject JTuner::openTimeFilter() { if (mDemuxClient == nullptr) { return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject timeFilterObj = env->NewObject( env->FindClass("android/media/tv/tuner/filter/TimeFilter"), gFields.timeFilterInitID); sp timeFilterClient = mDemuxClient->openTimeFilter(); if (timeFilterClient == nullptr) { ALOGD("Failed to open time filter."); return nullptr; } timeFilterClient->incStrong(timeFilterObj); env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterClient.get()); return timeFilterObj; } jobject JTuner::openDvr(DvrType type, jlong bufferSize) { ALOGV("JTuner::openDvr"); if (mDemuxClient == nullptr) { return nullptr; } sp dvrClient; sp callback = new DvrClientCallbackImpl(); dvrClient = mDemuxClient->openDvr(type, (int) bufferSize, callback); if (dvrClient == nullptr) { ALOGD("Failed to open Dvr"); return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject dvrObj; if (type == DvrType::RECORD) { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"), gFields.dvrRecorderInitID); dvrClient->incStrong(dvrObj); env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrClient.get()); } else { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"), gFields.dvrPlaybackInitID); dvrClient->incStrong(dvrObj); env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrClient.get()); } callback->setDvr(env->NewWeakGlobalRef(dvrObj)); return dvrObj; } jobject JTuner::getDemuxCaps() { if (sTunerClient == nullptr) { return nullptr; } shared_ptr caps; caps = sTunerClient->getDemuxCaps(); if (caps == nullptr) { return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "", "(IIIIIIIIIJI[I[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; jboolean bTimeFilter = caps->bTimeFilter; jint filterCaps = caps->filterCaps; jintArray filterCapsList = nullptr; vector demuxInfoList; sTunerClient->getDemuxInfoList(&demuxInfoList); if (demuxInfoList.size() > 0) { vector demuxFilterTypesList; for (int i = 0; i < demuxInfoList.size(); i++) { demuxFilterTypesList.push_back(demuxInfoList[i].filterTypes); } filterCapsList = env->NewIntArray(demuxFilterTypesList.size()); env->SetIntArrayRegion(filterCapsList, 0, demuxFilterTypesList.size(), reinterpret_cast(&demuxFilterTypesList[0])); } else { filterCapsList = env->NewIntArray(0); } 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, filterCapsList, linkCaps, bTimeFilter); } jobject JTuner::getDemuxInfo(int handle) { if (sTunerClient == nullptr) { ALOGE("tuner is not initialized"); return nullptr; } shared_ptr demuxInfo = sTunerClient->getDemuxInfo(handle); if (demuxInfo == nullptr) { return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/DemuxInfo"); jmethodID infoInit = env->GetMethodID(clazz, "", "(I)V"); jint filterTypes = demuxInfo->filterTypes; return env->NewObject(clazz, infoInit, filterTypes); } jobject JTuner::getFrontendStatus(jintArray types) { if (mFeClient == nullptr) { return nullptr; } JNIEnv *env = AndroidRuntime::getJNIEnv(); jsize size = env->GetArrayLength(types); jint intTypes[size]; env->GetIntArrayRegion(types, 0, size, intTypes); std::vector v; for (int i = 0; i < size; i++) { v.push_back(static_cast(intTypes[i])); } vector status = mFeClient->getStatus(v); 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"); jclass longClazz = env->FindClass("java/lang/Long"); jmethodID initLong = env->GetMethodID(longClazz, "", "(J)V"); for (int i = 0; i < status.size(); i++) { const FrontendStatus &s = status[i]; switch (s.getTag()) { case FrontendStatus::Tag::isDemodLocked: { jfieldID field = env->GetFieldID(clazz, "mIsDemodLocked", "Ljava/lang/Boolean;"); ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean, s.get())); env->SetObjectField(statusObj, field, newBooleanObj.get()); break; } case FrontendStatus::Tag::snr: { jfieldID field = env->GetFieldID(clazz, "mSnr", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::ber: { jfieldID field = env->GetFieldID(clazz, "mBer", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::per: { jfieldID field = env->GetFieldID(clazz, "mPer", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::preBer: { jfieldID field = env->GetFieldID(clazz, "mPerBer", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::signalQuality: { jfieldID field = env->GetFieldID(clazz, "mSignalQuality", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::signalStrength: { jfieldID field = env->GetFieldID(clazz, "mSignalStrength", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::symbolRate: { jfieldID field = env->GetFieldID(clazz, "mSymbolRate", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::innerFec: { jfieldID field = env->GetFieldID(clazz, "mInnerFec", "Ljava/lang/Long;"); ScopedLocalRef longClazz(env, env->FindClass("java/lang/Long")); jmethodID initLong = env->GetMethodID(longClazz.get(), "", "(J)V"); ScopedLocalRef newLongObj(env, env->NewObject(longClazz.get(), initLong, static_cast(s.get()))); env->SetObjectField(statusObj, field, newLongObj.get()); break; } case FrontendStatus::Tag::modulationStatus: { jfieldID field = env->GetFieldID(clazz, "mModulation", "Ljava/lang/Integer;"); FrontendModulationStatus modulation = s.get(); jint intModulation; bool valid = true; switch (modulation.getTag()) { case FrontendModulationStatus::Tag::dvbc: { intModulation = static_cast( modulation.get()); break; } case FrontendModulationStatus::Tag::dvbs: { intModulation = static_cast( modulation.get()); break; } case FrontendModulationStatus::Tag::isdbs: { intModulation = static_cast( modulation.get()); break; } case FrontendModulationStatus::Tag::isdbs3: { intModulation = static_cast( modulation.get()); break; } case FrontendModulationStatus::Tag::isdbt: { intModulation = static_cast( modulation.get()); break; } default: { valid = false; break; } } if (valid) { ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, intModulation)); env->SetObjectField(statusObj, field, newIntegerObj.get()); } break; } case FrontendStatus::Tag::inversion: { jfieldID field = env->GetFieldID(clazz, "mInversion", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, static_cast(s.get()))); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::lnbVoltage: { jfieldID field = env->GetFieldID(clazz, "mLnbVoltage", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, static_cast(s.get()))); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::plpId: { jfieldID field = env->GetFieldID(clazz, "mPlpId", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::isEWBS: { jfieldID field = env->GetFieldID(clazz, "mIsEwbs", "Ljava/lang/Boolean;"); ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean, s.get())); env->SetObjectField(statusObj, field, newBooleanObj.get()); break; } case FrontendStatus::Tag::agc: { jfieldID field = env->GetFieldID(clazz, "mAgc", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::isLnaOn: { jfieldID field = env->GetFieldID(clazz, "mIsLnaOn", "Ljava/lang/Boolean;"); ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean, s.get())); env->SetObjectField(statusObj, field, newBooleanObj.get()); break; } case FrontendStatus::Tag::isLayerError: { jfieldID field = env->GetFieldID(clazz, "mIsLayerErrors", "[Z"); vector layerErr = s.get(); ScopedLocalRef valObj(env, env->NewBooleanArray(layerErr.size())); for (size_t i = 0; i < layerErr.size(); i++) { jboolean x = layerErr[i]; env->SetBooleanArrayRegion(valObj.get(), i, 1, &x); } env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::mer: { jfieldID field = env->GetFieldID(clazz, "mMer", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::freqOffset: { jfieldID field = env->GetFieldID(clazz, "mFreqOffset", "Ljava/lang/Long;"); ScopedLocalRef newLongObj(env, env->NewObject(longClazz, initLong, s.get())); env->SetObjectField(statusObj, field, newLongObj.get()); break; } case FrontendStatus::Tag::hierarchy: { jfieldID field = env->GetFieldID(clazz, "mHierarchy", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, static_cast(s.get()))); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::isRfLocked: { jfieldID field = env->GetFieldID(clazz, "mIsRfLocked", "Ljava/lang/Boolean;"); ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean, s.get())); env->SetObjectField(statusObj, field, newBooleanObj.get()); break; } case FrontendStatus::Tag::plpInfo: { jfieldID field = env->GetFieldID(clazz, "mPlpInfo", "[Landroid/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo;"); ScopedLocalRef plpClazz(env, env->FindClass( "android/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo")); jmethodID initPlp = env->GetMethodID(plpClazz.get(), "", "(IZI)V"); vector plpInfos = s.get(); ScopedLocalRef valObj(env, env->NewObjectArray(plpInfos.size(), plpClazz.get(), nullptr)); for (int i = 0; i < plpInfos.size(); i++) { const FrontendStatusAtsc3PlpInfo &info = plpInfos[i]; jint plpId = info.plpId; jboolean isLocked = info.isLocked; jint uec = info.uec; ScopedLocalRef plpObj(env, env->NewObject(plpClazz.get(), initPlp, plpId, isLocked, uec)); env->SetObjectArrayElement(valObj.get(), i, plpObj.get()); } env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::modulations: { jfieldID field = env->GetFieldID(clazz, "mModulationsExt", "[I"); std::vector v = s.get(); ScopedLocalRef valObj(env, env->NewIntArray(v.size())); bool valid = false; jint m[1]; for (int i = 0; i < v.size(); i++) { const FrontendModulation &modulation = v[i]; switch (modulation.getTag()) { case FrontendModulation::Tag::dvbc: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } case FrontendModulation::Tag::dvbs: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } case FrontendModulation::Tag::dvbt: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } case FrontendModulation::Tag::isdbs: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } case FrontendModulation::Tag::isdbs3: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } case FrontendModulation::Tag::isdbt: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } case FrontendModulation::Tag::atsc: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } case FrontendModulation::Tag::atsc3: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } case FrontendModulation::Tag::dtmb: { m[0] = static_cast( modulation.get()); env->SetIntArrayRegion(valObj.get(), i, 1, m); valid = true; break; } default: break; } } if (valid) { env->SetObjectField(statusObj, field, valObj.get()); } break; } case FrontendStatus::Tag::bers: { jfieldID field = env->GetFieldID(clazz, "mBers", "[I"); std::vector v = s.get(); ScopedLocalRef valObj(env, env->NewIntArray(v.size())); env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast(&v[0])); env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::codeRates: { jfieldID field = env->GetFieldID(clazz, "mCodeRates", "[I"); std::vector v = s.get(); ScopedLocalRef valObj(env, env->NewIntArray(v.size())); env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast(&v[0])); env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::bandwidth: { jfieldID field = env->GetFieldID(clazz, "mBandwidth", "Ljava/lang/Integer;"); const FrontendBandwidth &bandwidth = s.get(); jint intBandwidth; bool valid = true; switch (bandwidth.getTag()) { case FrontendBandwidth::Tag::atsc3: { intBandwidth = static_cast(bandwidth.get()); break; } case FrontendBandwidth::Tag::dvbt: { intBandwidth = static_cast(bandwidth.get()); break; } case FrontendBandwidth::Tag::dvbc: { intBandwidth = static_cast(bandwidth.get()); break; } case FrontendBandwidth::Tag::isdbt: { intBandwidth = static_cast(bandwidth.get()); break; } case FrontendBandwidth::Tag::dtmb: { intBandwidth = static_cast(bandwidth.get()); break; } default: valid = false; break; } if (valid) { ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, intBandwidth)); env->SetObjectField(statusObj, field, newIntegerObj.get()); } break; } case FrontendStatus::Tag::interval: { jfieldID field = env->GetFieldID(clazz, "mGuardInterval", "Ljava/lang/Integer;"); const FrontendGuardInterval &interval = s.get(); jint intInterval; bool valid = true; switch (interval.getTag()) { case FrontendGuardInterval::Tag::dvbt: { intInterval = static_cast( interval.get()); break; } case FrontendGuardInterval::Tag::isdbt: { intInterval = static_cast( interval.get()); break; } case FrontendGuardInterval::Tag::dtmb: { intInterval = static_cast( interval.get()); break; } default: valid = false; break; } if (valid) { ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, intInterval)); env->SetObjectField(statusObj, field, newIntegerObj.get()); } break; } case FrontendStatus::Tag::transmissionMode: { jfieldID field = env->GetFieldID(clazz, "mTransmissionMode", "Ljava/lang/Integer;"); const FrontendTransmissionMode &transmissionMode = s.get(); jint intTransmissionMode; bool valid = true; switch (transmissionMode.getTag()) { case FrontendTransmissionMode::Tag::dvbt: { intTransmissionMode = static_cast( transmissionMode.get()); break; } case FrontendTransmissionMode::Tag::isdbt: { intTransmissionMode = static_cast( transmissionMode.get()); break; } case FrontendTransmissionMode::Tag::dtmb: { intTransmissionMode = static_cast( transmissionMode.get()); break; } default: valid = false; break; } if (valid) { ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, intTransmissionMode)); env->SetObjectField(statusObj, field, newIntegerObj.get()); } break; } case FrontendStatus::Tag::uec: { jfieldID field = env->GetFieldID(clazz, "mUec", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::systemId: { jfieldID field = env->GetFieldID(clazz, "mSystemId", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::interleaving: { jfieldID field = env->GetFieldID(clazz, "mInterleaving", "[I"); std::vector v = s.get(); ScopedLocalRef valObj(env, env->NewIntArray(v.size())); bool valid = false; jint in[1]; for (int i = 0; i < v.size(); i++) { const FrontendInterleaveMode &interleaving = v[i]; switch (interleaving.getTag()) { case FrontendInterleaveMode::Tag::atsc3: { in[0] = static_cast( interleaving.get()); env->SetIntArrayRegion(valObj.get(), i, 1, in); valid = true; break; } case FrontendInterleaveMode::Tag::dvbc: { in[0] = static_cast( interleaving.get()); env->SetIntArrayRegion(valObj.get(), i, 1, in); valid = true; break; } case FrontendInterleaveMode::Tag::dtmb: { in[0] = static_cast( interleaving.get()); env->SetIntArrayRegion(valObj.get(), i, 1, in); valid = true; break; } case FrontendInterleaveMode::Tag::isdbt: { in[0] = static_cast( interleaving.get()); env->SetIntArrayRegion(valObj.get(), i, 1, in); valid = true; break; } default: break; } } if (valid) { env->SetObjectField(statusObj, field, valObj.get()); } break; } case FrontendStatus::Tag::isdbtSegment: { jfieldID field = env->GetFieldID(clazz, "mIsdbtSegment", "[I"); std::vector v = s.get(); ScopedLocalRef valObj(env, env->NewIntArray(v.size())); env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast(&v[0])); env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::tsDataRate: { jfieldID field = env->GetFieldID(clazz, "mTsDataRate", "[I"); std::vector v = s.get(); ScopedLocalRef valObj(env, env->NewIntArray(v.size())); env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast(&v[0])); env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::rollOff: { jfieldID field = env->GetFieldID(clazz, "mRollOff", "Ljava/lang/Integer;"); const FrontendRollOff &rollOff = s.get(); jint intRollOff; bool valid = true; switch (rollOff.getTag()) { case FrontendRollOff::Tag::dvbs: { intRollOff = static_cast(rollOff.get()); break; } case FrontendRollOff::Tag::isdbs: { intRollOff = static_cast(rollOff.get()); break; } case FrontendRollOff::Tag::isdbs3: { intRollOff = static_cast( rollOff.get()); break; } default: valid = false; break; } if (valid) { ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, intRollOff)); env->SetObjectField(statusObj, field, newIntegerObj.get()); } break; } case FrontendStatus::Tag::isMiso: { jfieldID field = env->GetFieldID(clazz, "mIsMisoEnabled", "Ljava/lang/Boolean;"); ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean, s.get())); env->SetObjectField(statusObj, field, newBooleanObj.get()); break; } case FrontendStatus::Tag::isLinear: { jfieldID field = env->GetFieldID(clazz, "mIsLinear", "Ljava/lang/Boolean;"); ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean, s.get())); env->SetObjectField(statusObj, field, newBooleanObj.get()); break; } case FrontendStatus::Tag::isShortFrames: { jfieldID field = env->GetFieldID(clazz, "mIsShortFrames", "Ljava/lang/Boolean;"); ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean, s.get())); env->SetObjectField(statusObj, field, newBooleanObj.get()); break; } case FrontendStatus::Tag::isdbtMode: { jfieldID field = env->GetFieldID(clazz, "mIsdbtMode", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::partialReceptionFlag: { jfieldID field = env->GetFieldID(clazz, "mIsdbtPartialReceptionFlag", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::streamIdList: { jfieldID field = env->GetFieldID(clazz, "mStreamIds", "[I"); std::vector ids = s.get(); ScopedLocalRef valObj(env, env->NewIntArray(v.size())); env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast(&ids[0])); env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::dvbtCellIds: { jfieldID field = env->GetFieldID(clazz, "mDvbtCellIds", "[I"); std::vector ids = s.get(); ScopedLocalRef valObj(env, env->NewIntArray(v.size())); env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast(&ids[0])); env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::allPlpInfo: { jfieldID field = env->GetFieldID(clazz, "mAllPlpInfo", "[Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;"); ScopedLocalRef plpClazz(env, env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo")); jmethodID initPlp = env->GetMethodID(plpClazz.get(), "", "(IZ)V"); vector plpInfos = s.get(); ScopedLocalRef valObj(env, env->NewObjectArray(plpInfos.size(), plpClazz.get(), nullptr)); for (int i = 0; i < plpInfos.size(); i++) { ScopedLocalRef plpObj(env, env->NewObject(plpClazz.get(), initPlp, plpInfos[i].plpId, plpInfos[i].bLlsFlag)); env->SetObjectArrayElement(valObj.get(), i, plpObj.get()); } env->SetObjectField(statusObj, field, valObj.get()); break; } case FrontendStatus::Tag::iptvContentUrl: { jfieldID field = env->GetFieldID(clazz, "mIptvContentUrl", "Ljava/lang/String;"); std::string iptvContentUrl = s.get(); ScopedLocalRef iptvContentUrlUtf8(env, env->NewStringUTF(iptvContentUrl.c_str())); env->SetObjectField(statusObj, field, iptvContentUrlUtf8.get()); break; } case FrontendStatus::Tag::iptvPacketsLost: { jfieldID field = env->GetFieldID(clazz, "mIptvPacketsLost", "Ljava/lang/Long;"); ScopedLocalRef newLongObj(env, env->NewObject(longClazz, initLong, s.get())); env->SetObjectField(statusObj, field, newLongObj.get()); break; } case FrontendStatus::Tag::iptvPacketsReceived: { jfieldID field = env->GetFieldID(clazz, "mIptvPacketsReceived", "Ljava/lang/Long;"); ScopedLocalRef newLongObj(env, env->NewObject(longClazz, initLong, s.get())); env->SetObjectField(statusObj, field, newLongObj.get()); break; } case FrontendStatus::Tag::iptvWorstJitterMs: { jfieldID field = env->GetFieldID(clazz, "mIptvWorstJitterMs", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } case FrontendStatus::Tag::iptvAverageJitterMs: { jfieldID field = env->GetFieldID(clazz, "mIptvAverageJitterMs", "Ljava/lang/Integer;"); ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt, s.get())); env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } } } return statusObj; } jint JTuner::closeFrontend() { Result r = Result::SUCCESS; if (mFeClient != nullptr) { r = mFeClient->close(); } if (r == Result::SUCCESS) { mFeClient = nullptr; } return (jint)r; } jint JTuner::closeDemux() { Result r = Result::SUCCESS; if (mDemuxClient != nullptr) { r = mDemuxClient->close(); } if (r == Result::SUCCESS) { mDemuxClient = nullptr; } 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 != nullptr) { tuner->incStrong(thiz); } if (old != nullptr) { old->decStrong(thiz); } if (tuner != nullptr) { 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 getDescramblerClient(JNIEnv *env, jobject descrambler) { return (DescramblerClient *)env->GetLongField(descrambler, gFields.descramblerContext); } static DemuxPid getDemuxPid(int pidType, int pid) { DemuxPid demuxPid; if (pidType == 1) { demuxPid.set(pid); } else if (pidType == 2) { demuxPid.set(pid); } return demuxPid; } static int64_t getFrontendSettingsFreq(JNIEnv *env, const jobject &settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings"); jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "J"); return env->GetLongField(settings, freqField); } static int64_t getFrontendSettingsEndFreq(JNIEnv *env, const jobject &settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings"); jfieldID endFreqField = env->GetFieldID(clazz, "mEndFrequency", "J"); return env->GetLongField(settings, endFreqField); } static FrontendSpectralInversion getFrontendSettingsSpectralInversion( JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings"); jfieldID inversionField = env->GetFieldID(clazz, "mSpectralInversion", "I"); FrontendSpectralInversion inversion = static_cast(env->GetIntField(settings, inversionField)); return inversion; } static FrontendSettings getAnalogFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(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"))); FrontendAnalogAftFlag aftFlag = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mAftFlag", "I"))); FrontendAnalogSettings frontendAnalogSettings{ .frequency = freq, .endFrequency = endFreq, .type = analogType, .sifStandard = sifStandard, .aftFlag = aftFlag, .inversion = inversion, }; frontendSettings.set(frontendAnalogSettings); return frontendSettings; } static vector 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"); vector plps = vector(len); // parse PLP settings for (int i = 0; i < len; i++) { ScopedLocalRef plp(env, env->GetObjectArrayElement(plpSettings, i)); int32_t plpId = env->GetIntField(plp.get(), env->GetFieldID(plpClazz, "mPlpId", "I")); FrontendAtsc3Modulation modulation = static_cast( env->GetIntField(plp.get(), env->GetFieldID(plpClazz, "mModulation", "I"))); FrontendAtsc3TimeInterleaveMode interleaveMode = static_cast( env->GetIntField( plp.get(), env->GetFieldID(plpClazz, "mInterleaveMode", "I"))); FrontendAtsc3CodeRate codeRate = static_cast( env->GetIntField(plp.get(), env->GetFieldID(plpClazz, "mCodeRate", "I"))); FrontendAtsc3Fec fec = static_cast( env->GetIntField(plp.get(), 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; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(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"))); vector plps = getAtsc3PlpSettings(env, settings); FrontendAtsc3Settings frontendAtsc3Settings{ .frequency = freq, .endFrequency = endFreq, .bandwidth = bandwidth, .demodOutputFormat = demod, .plpSettings = plps, .inversion = inversion, }; frontendSettings.set(frontendAtsc3Settings); return frontendSettings; } static FrontendSettings getAtscFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(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, .endFrequency = endFreq, .modulation = modulation, .inversion = inversion, }; frontendSettings.set(frontendAtscSettings); return frontendSettings; } static FrontendSettings getDvbcFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(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, "mInnerFec", "J"))); int32_t symbolRate = 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->GetIntField(settings, env->GetFieldID(clazz, "mAnnex", "I"))); FrontendSpectralInversion spectralInversion = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mSpectralInversion", "I"))); FrontendCableTimeInterleaveMode interleaveMode = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mInterleaveMode", "I"))); FrontendDvbcBandwidth bandwidth = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I"))); FrontendDvbcSettings frontendDvbcSettings{ .frequency = freq, .endFrequency = endFreq, .modulation = modulation, .fec = innerFec, .symbolRate = symbolRate, .outerFec = outerFec, .annex = annex, .inversion = spectralInversion, .interleaveMode = interleaveMode, .bandwidth = bandwidth, }; frontendSettings.set(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 = env->GetBooleanField(jcodeRate, env->GetFieldID(codeRateClazz, "mIsLinear", "Z")); bool isShortFrames = env->GetBooleanField(jcodeRate, env->GetFieldID(codeRateClazz, "mIsShortFrames", "Z")); int32_t bitsPer1000Symbol = 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; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings"); FrontendDvbsModulation modulation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); int32_t symbolRate = 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"))); int32_t inputStreamId = 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"))); FrontendDvbsScanType scanType = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I"))); bool isDiseqcRxMessage = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "Z")); FrontendDvbsSettings frontendDvbsSettings{ .frequency = freq, .endFrequency = endFreq, .modulation = modulation, .symbolRate = symbolRate, .rolloff = rolloff, .pilot = pilot, .inputStreamId = inputStreamId, .standard = standard, .vcmMode = vcmMode, .scanType = scanType, .isDiseqcRxMessage = isDiseqcRxMessage, .inversion = inversion, }; jobject jcodeRate = env->GetObjectField(settings, env->GetFieldID(clazz, "mCodeRate", "Landroid/media/tv/tuner/frontend/DvbsCodeRate;")); if (jcodeRate != nullptr) { frontendDvbsSettings.coderate = getDvbsCodeRate(env, settings); } frontendSettings.set(frontendDvbsSettings); return frontendSettings; } static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(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 = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsHighPriority", "Z")); FrontendDvbtStandard standard = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mStandard", "I"))); bool isMiso = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsMiso", "Z")); FrontendDvbtPlpMode plpMode = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mPlpMode", "I"))); int32_t plpId = env->GetIntField(settings, env->GetFieldID(clazz, "mPlpId", "I")); int32_t plpGroupId = env->GetIntField(settings, env->GetFieldID(clazz, "mPlpGroupId", "I")); FrontendDvbtSettings frontendDvbtSettings{ .frequency = freq, .endFrequency = endFreq, .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, .inversion = inversion, }; frontendSettings.set(frontendDvbtSettings); return frontendSettings; } static FrontendSettings getIsdbsFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendSettings"); int32_t streamId = 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"))); int32_t symbolRate = env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I")); FrontendIsdbsRolloff rolloff = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mRolloff", "I"))); FrontendIsdbsSettings frontendIsdbsSettings{ .frequency = freq, .endFrequency = endFreq, .streamId = streamId, .streamIdType = streamIdType, .modulation = modulation, .coderate = coderate, .symbolRate = symbolRate, .rolloff = rolloff, }; frontendSettings.set(frontendIsdbsSettings); return frontendSettings; } static FrontendSettings getIsdbs3FrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendSettings"); int32_t streamId = 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"))); int32_t symbolRate = env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I")); FrontendIsdbs3Rolloff rolloff = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mRolloff", "I"))); FrontendIsdbs3Settings frontendIsdbs3Settings{ .frequency = freq, .endFrequency = endFreq, .streamId = streamId, .streamIdType = streamIdType, .modulation = modulation, .coderate = coderate, .symbolRate = symbolRate, .rolloff = rolloff, }; frontendSettings.set(frontendIsdbs3Settings); return frontendSettings; } static FrontendSettings getIsdbtFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendSettings"); FrontendIsdbtBandwidth bandwidth = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I"))); FrontendIsdbtMode mode = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mMode", "I"))); FrontendIsdbtGuardInterval guardInterval = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I"))); int32_t serviceAreaId = env->GetIntField(settings, env->GetFieldID(clazz, "mServiceAreaId", "I")); FrontendIsdbtPartialReceptionFlag partialReceptionFlag = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mPartialReceptionFlag", "I"))); FrontendIsdbtSettings frontendIsdbtSettings{ .frequency = freq, .endFrequency = endFreq, .bandwidth = bandwidth, .mode = mode, .guardInterval = guardInterval, .serviceAreaId = serviceAreaId, .inversion = inversion, .partialReceptionFlag = partialReceptionFlag, }; jobjectArray layerSettings = reinterpret_cast( env->GetObjectField(settings, env->GetFieldID(clazz, "mLayerSettings", "[Landroid/media/tv/tuner/frontend/" "IsdbtFrontendSettings$IsdbtLayerSettings;"))); int len = env->GetArrayLength(layerSettings); jclass layerClazz = env->FindClass( "android/media/tv/tuner/frontend/IsdbtFrontendSettings$IsdbtLayerSettings"); frontendIsdbtSettings.layerSettings.resize(len); for (int i = 0; i < len; i++) { ScopedLocalRef layer(env, env->GetObjectArrayElement(layerSettings, i)); frontendIsdbtSettings.layerSettings[i].modulation = static_cast( env->GetIntField(layer.get(), env->GetFieldID(layerClazz, "mModulation", "I"))); frontendIsdbtSettings.layerSettings[i].timeInterleave = static_cast( env->GetIntField(layer.get(), env->GetFieldID(layerClazz, "mTimeInterleaveMode", "I"))); frontendIsdbtSettings.layerSettings[i].coderate = static_cast( env->GetIntField(layer.get(), env->GetFieldID(layerClazz, "mCodeRate", "I"))); frontendIsdbtSettings.layerSettings[i].numOfSegment = env->GetIntField(layer.get(), env->GetFieldID(layerClazz, "mNumOfSegments", "I")); } frontendSettings.set(frontendIsdbtSettings); return frontendSettings; } static FrontendSettings getDtmbFrontendSettings(JNIEnv *env, const jobject &settings) { FrontendSettings frontendSettings; int64_t freq = getFrontendSettingsFreq(env, settings); int64_t endFreq = getFrontendSettingsEndFreq(env, settings); FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendSettings"); FrontendDtmbModulation modulation = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); FrontendDtmbBandwidth bandwidth = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I"))); FrontendDtmbTransmissionMode transmissionMode = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mTransmissionMode", "I"))); FrontendDtmbCodeRate codeRate = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I"))); FrontendDtmbGuardInterval guardInterval = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I"))); FrontendDtmbTimeInterleaveMode interleaveMode = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mTimeInterleaveMode", "I"))); FrontendDtmbSettings frontendDtmbSettings{ .frequency = freq, .endFrequency = endFreq, .modulation = modulation, .bandwidth = bandwidth, .transmissionMode = transmissionMode, .codeRate = codeRate, .guardInterval = guardInterval, .interleaveMode = interleaveMode, .inversion = inversion, }; frontendSettings.set(frontendDtmbSettings); return frontendSettings; } static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config, const char* className) { jclass clazz = env->FindClass(className); 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) { vector srcAddr; vector dstAddr; srcAddr.resize(IP_V4_LENGTH); dstAddr.resize(IP_V4_LENGTH); env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast(&srcAddr[0])); env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast(&dstAddr[0])); res.srcIpAddress.set(srcAddr); res.dstIpAddress.set(dstAddr); } else if (srcSize == IP_V6_LENGTH) { vector srcAddr; vector dstAddr; srcAddr.resize(IP_V6_LENGTH); dstAddr.resize(IP_V6_LENGTH); env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast(&srcAddr[0])); env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast(&dstAddr[0])); res.srcIpAddress.set(srcAddr); res.dstIpAddress.set(dstAddr); } else { // should never happen. Validated on Java size. jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid IP address length %d", srcSize); return res; } res.srcPort = env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I")); res.dstPort = env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I")); return res; } static FrontendIptvSettingsFec getIptvFrontendSettingsFec(JNIEnv *env, const jobject &settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendSettings"); jclass fecClazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendSettingsFec"); jobject fec = env->GetObjectField(settings, env->GetFieldID(clazz, "mFec", "Landroid/media/tv/tuner/frontend/IptvFrontendSettingsFec;")); FrontendIptvSettingsFecType type = static_cast( env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecType", "I"))); int32_t fecColNum = env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecColNum", "I")); int32_t fecRowNum = env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecRowNum", "I")); FrontendIptvSettingsFec frontendIptvSettingsFec = { .type = type, .fecColNum = fecColNum, .fecRowNum = fecRowNum, }; return frontendIptvSettingsFec; } static FrontendSettings getIptvFrontendSettings(JNIEnv *env, const jobject &settings) { FrontendSettings frontendSettings; const char *className = "android/media/tv/tuner/frontend/IptvFrontendSettings"; jclass clazz = env->FindClass(className); FrontendIptvSettingsProtocol protocol = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mProtocol", "I"))); FrontendIptvSettingsIgmp igmp = static_cast( env->GetIntField(settings, env->GetFieldID(clazz, "mIgmp", "I"))); int64_t bitrate = env->GetIntField(settings, env->GetFieldID(clazz, "mBitrate", "J")); jstring jContentUrl = (jstring) env->GetObjectField(settings, env->GetFieldID( clazz, "mContentUrl", "Ljava/lang/String;")); const char *contentUrl = env->GetStringUTFChars(jContentUrl, 0); DemuxIpAddress ipAddr = getDemuxIpAddress(env, settings, className); FrontendIptvSettings frontendIptvSettings{ .protocol = protocol, .igmp = igmp, .bitrate = bitrate, .ipAddr = ipAddr, .contentUrl = contentUrl, }; jobject jFec = env->GetObjectField(settings, env->GetFieldID(clazz, "mFec", "Landroid/media/tv/tuner/frontend/IptvFrontendSettingsFec;")); if (jFec != nullptr) { frontendIptvSettings.fec = getIptvFrontendSettingsFec(env, settings); } frontendSettings.set(frontendIptvSettings); env->ReleaseStringUTFChars(jContentUrl, contentUrl); return frontendSettings; } static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) { ALOGV("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); case FrontendType::DTMB: return getDtmbFrontendSettings(env, settings); case FrontendType::IPTV: return getIptvFrontendSettings(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 getFilterClient(JNIEnv *env, jobject filter) { return (FilterClient *)env->GetLongField(filter, gFields.filterContext); } static sp getSharedFilterClient(JNIEnv *env, jobject filter) { return (FilterClient *)env->GetLongField(filter, gFields.sharedFilterContext); } static sp getLnbClient(JNIEnv *env, jobject lnb) { return (LnbClient *)env->GetLongField(lnb, gFields.lnbContext); } static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder) { DvrSettings dvrSettings; jclass clazz = env->FindClass("android/media/tv/tuner/dvr/DvrSettings"); int32_t statusMask = env->GetIntField(settings, env->GetFieldID(clazz, "mStatusMask", "I")); int64_t lowThreshold = env->GetLongField(settings, env->GetFieldID(clazz, "mLowThreshold", "J")); int64_t highThreshold = env->GetLongField(settings, env->GetFieldID(clazz, "mHighThreshold", "J")); int64_t packetSize = 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 = statusMask, .lowThreshold = lowThreshold, .highThreshold = highThreshold, .dataFormat = dataFormat, .packetSize = packetSize, }; dvrSettings.set(recordSettings); } else { PlaybackSettings PlaybackSettings { .statusMask = statusMask, .lowThreshold = lowThreshold, .highThreshold = highThreshold, .dataFormat = dataFormat, .packetSize = packetSize, }; dvrSettings.set(PlaybackSettings); } return dvrSettings; } static sp getDvrClient(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 (DvrClient *)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 != nullptr); gFields.tunerContext = env->GetFieldID(clazz, "mNativeContext", "J"); CHECK(gFields.tunerContext != nullptr); 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, "", "()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, "", "(J)V"); gFields.onFilterStatusID = env->GetMethodID(filterClazz, "onFilterStatus", "(I)V"); gFields.onFilterEventID = env->GetMethodID(filterClazz, "onFilterEvent", "([Landroid/media/tv/tuner/filter/FilterEvent;)V"); jclass sharedFilterClazz = env->FindClass("android/media/tv/tuner/filter/SharedFilter"); gFields.sharedFilterContext = env->GetFieldID(sharedFilterClazz, "mNativeContext", "J"); gFields.sharedFilterInitID = env->GetMethodID(sharedFilterClazz, "", "()V"); gFields.onSharedFilterStatusID = env->GetMethodID(sharedFilterClazz, "onFilterStatus", "(I)V"); gFields.onSharedFilterEventID = env->GetMethodID(sharedFilterClazz, "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"); gAudioPresentationFields.init(env); } static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) { sp tuner = new JTuner(env, thiz); setTuner(env, thiz, tuner); } static jint android_media_tv_Tuner_native_get_tuner_version(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->getTunerVersion(); } 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); return tuner->openFrontendByHandle(handle); } static int android_media_tv_Tuner_share_frontend( JNIEnv *env, jobject thiz, jint id) { sp tuner = getTuner(env, thiz); return tuner->shareFrontend(id); } static int android_media_tv_Tuner_unshare_frontend( JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->unshareFrontend(); } static void android_media_tv_Tuner_register_fe_cb_listener( JNIEnv *env, jobject thiz, jlong shareeJTuner) { sp tuner = getTuner(env, thiz); JTuner *jtuner = (JTuner *)shareeJTuner; tuner->registerFeCbListener(jtuner); } static void android_media_tv_Tuner_unregister_fe_cb_listener( JNIEnv *env, jobject thiz, jlong shareeJTuner) { sp tuner = getTuner(env, thiz); JTuner *jtuner = (JTuner *)shareeJTuner; tuner->unregisterFeCbListener(jtuner); } static void android_media_tv_Tuner_update_frontend(JNIEnv *env, jobject thiz, jlong jtunerPtr) { sp tuner = getTuner(env, thiz); JTuner *jtuner; if (jtunerPtr == 0) { jtuner = nullptr; } else { jtuner = (JTuner *) jtunerPtr; } tuner->updateFrontend(jtuner); } static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) { sp tuner = getTuner(env, thiz); FrontendSettings setting = getFrontendSettings(env, type, settings); return tuner->tune(setting); } 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); FrontendSettings setting = getFrontendSettings(env, settingsType, settings); return tuner->scan(setting, 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, jobject lnb) { sp tuner = getTuner(env, thiz); sp lnbClient = getLnbClient(env, lnb); if (lnbClient == nullptr) { ALOGE("lnb is not initialized"); return (int)Result::INVALID_STATE; } return tuner->setLnb(lnbClient); } static bool android_media_tv_Tuner_is_lna_supported(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->isLnaSupported(); } 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 filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { ALOGD("Failed to get sync ID. Filter client not found"); return nullptr; } sp tuner = getTuner(env, thiz); return tuner->getAvSyncHwId(filterClient); } 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_link_cicam(JNIEnv *env, jobject thiz, jint id) { sp tuner = getTuner(env, thiz); return tuner->linkCiCam(id); } static int android_media_tv_Tuner_disconnect_cicam(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); return tuner->disconnectCiCam(); } static int android_media_tv_Tuner_unlink_cicam(JNIEnv *env, jobject thiz, jint id) { sp tuner = getTuner(env, thiz); return tuner->unlinkCiCam(id); } 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 jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) { sp tuner = getTuner(env, thiz); return tuner->openLnbByHandle(handle); } 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.set( static_cast(subType)); break; case DemuxFilterMainType::MMTP: filterType.subType.set( static_cast(subType)); break; case DemuxFilterMainType::IP: filterType.subType.set( static_cast(subType)); break; case DemuxFilterMainType::TLV: filterType.subType.set( static_cast(subType)); break; case DemuxFilterMainType::ALP: filterType.subType.set( static_cast(subType)); break; default: ALOGD("Demux Filter Main Type is undefined."); return nullptr; } 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 DemuxFilterSectionSettingsConditionTableInfo getFilterTableInfo(JNIEnv *env, const jobject &settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"); int32_t tableId = env->GetIntField(settings, env->GetFieldID(clazz, "mTableId", "I")); int32_t version = env->GetIntField(settings, env->GetFieldID(clazz, "mVersion", "I")); DemuxFilterSectionSettingsConditionTableInfo 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 = env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z")); bool isRepeat = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z")); bool isRaw = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z")); int32_t bitWidthOfLengthField = env->GetIntField(settings, env->GetFieldID(clazz, "mBitWidthOfLengthField", "I")); DemuxFilterSectionSettings filterSectionSettings { .isCheckCrc = isCheckCrc, .isRepeat = isRepeat, .isRaw = isRaw, .bitWidthOfLengthField = bitWidthOfLengthField, }; if (env->IsInstanceOf( settings, env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"))) { filterSectionSettings.condition.set( getFilterSectionBits(env, settings)); } else if (env->IsInstanceOf( settings, env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"))) { filterSectionSettings.condition.set( 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 = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z")); bool isSecureMemory = env->GetBooleanField(settings, env->GetFieldID(clazz, "mUseSecureMemory", "Z")); DemuxFilterAvSettings filterAvSettings{ .isPassthrough = isPassthrough, .isSecureMemory = isSecureMemory, }; return filterAvSettings; } static bool getAvStreamType(JNIEnv *env, jobject filterConfigObj, AvStreamType& type) { jobject settingsObj = env->GetObjectField( filterConfigObj, env->GetFieldID( env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"), "mSettings", "Landroid/media/tv/tuner/filter/Settings;")); jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings"); AvStreamType streamType; AudioStreamType audioStreamType = static_cast( env->GetIntField(settingsObj, env->GetFieldID(clazz, "mAudioStreamType", "I"))); if (audioStreamType != AudioStreamType::UNDEFINED) { type.set(audioStreamType); return true; } VideoStreamType videoStreamType = static_cast( env->GetIntField(settingsObj, env->GetFieldID(clazz, "mVideoStreamType", "I"))); if (videoStreamType != VideoStreamType::UNDEFINED) { type.set(videoStreamType); return true; } return false; } static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings"); int32_t streamId = env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I")); bool isRaw = 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"); int32_t tsIndexMask = 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")); // Backward compatibility for S- apps. if (scIndexType == DemuxRecordScIndexType::SC && scIndexMask > static_cast(DemuxScIndex::SEQUENCE)) { scIndexType = DemuxRecordScIndexType::SC_AVC; } DemuxFilterRecordSettings filterRecordSettings { .tsIndexMask = tsIndexMask, .scIndexType = scIndexType, }; if (scIndexType == DemuxRecordScIndexType::SC) { filterRecordSettings.scIndexMask.set(scIndexMask); } else if (scIndexType == DemuxRecordScIndexType::SC_HEVC) { filterRecordSettings.scIndexMask.set(scIndexMask); } else if (scIndexType == DemuxRecordScIndexType::SC_AVC) { // Java uses the values defined by HIDL HAL. Right shift 4 bits. filterRecordSettings.scIndexMask.set(scIndexMask >> 4); } else if (scIndexType == DemuxRecordScIndexType::SC_VVC) { filterRecordSettings.scIndexMask.set(scIndexMask); } return filterRecordSettings; } static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings"); bool useDownloadId = env->GetBooleanField(settings, env->GetFieldID(clazz, "mUseDownloadId", "Z")); int32_t downloadId = env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I")); DemuxFilterDownloadSettings filterDownloadSettings{ .useDownloadId = useDownloadId, .downloadId = downloadId, }; return filterDownloadSettings; } 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"); int32_t tpid = env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mTpid", "I")); DemuxTsFilterSettings tsFilterSettings { .tpid = tpid, }; if (settingsObj != nullptr) { DemuxTsFilterType tsType = static_cast(subtype); switch (tsType) { case DemuxTsFilterType::SECTION: tsFilterSettings.filterSettings .set( getFilterSectionSettings(env, settingsObj)); break; case DemuxTsFilterType::AUDIO: case DemuxTsFilterType::VIDEO: tsFilterSettings.filterSettings .set( getFilterAvSettings(env, settingsObj)); break; case DemuxTsFilterType::PES: tsFilterSettings.filterSettings .set( getFilterPesDataSettings(env, settingsObj)); break; case DemuxTsFilterType::RECORD: tsFilterSettings.filterSettings .set( getFilterRecordSettings(env, settingsObj)); break; default: break; } } filterSettings.set(tsFilterSettings); break; } case DemuxFilterMainType::MMTP: { jclass clazz = env->FindClass("android/media/tv/tuner/filter/MmtpFilterConfiguration"); int32_t mmtpPid = env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mMmtpPid", "I")); DemuxMmtpFilterSettings mmtpFilterSettings { .mmtpPid = mmtpPid, }; if (settingsObj != nullptr) { DemuxMmtpFilterType mmtpType = static_cast(subtype); switch (mmtpType) { case DemuxMmtpFilterType::SECTION: mmtpFilterSettings.filterSettings .set( getFilterSectionSettings(env, settingsObj)); break; case DemuxMmtpFilterType::AUDIO: case DemuxMmtpFilterType::VIDEO: mmtpFilterSettings.filterSettings .set( getFilterAvSettings(env, settingsObj)); break; case DemuxMmtpFilterType::PES: mmtpFilterSettings.filterSettings .set( getFilterPesDataSettings(env, settingsObj)); break; case DemuxMmtpFilterType::RECORD: mmtpFilterSettings.filterSettings .set( getFilterRecordSettings(env, settingsObj)); break; case DemuxMmtpFilterType::DOWNLOAD: mmtpFilterSettings.filterSettings .set( getFilterDownloadSettings(env, settingsObj)); break; default: break; } } filterSettings.set(mmtpFilterSettings); break; } case DemuxFilterMainType::IP: { DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj, "android/media/tv/tuner/filter/IpFilterConfiguration"); DemuxIpFilterSettings ipFilterSettings { .ipAddr = ipAddr, }; DemuxIpFilterType ipType = static_cast(subtype); if (ipType == DemuxIpFilterType::SECTION && settingsObj != nullptr) { ipFilterSettings.filterSettings .set( getFilterSectionSettings(env, settingsObj)); } else if (ipType == DemuxIpFilterType::IP) { jclass clazz = env->FindClass( "android/media/tv/tuner/filter/IpFilterConfiguration"); bool bPassthrough = env->GetBooleanField(filterConfigObj, env->GetFieldID(clazz, "mPassthrough", "Z")); ipFilterSettings.filterSettings .set(bPassthrough); } filterSettings.set(ipFilterSettings); break; } case DemuxFilterMainType::TLV: { jclass clazz = env->FindClass("android/media/tv/tuner/filter/TlvFilterConfiguration"); int32_t packetType = env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I")); bool isCompressedIpPacket = env->GetBooleanField(filterConfigObj, env->GetFieldID(clazz, "mIsCompressedIpPacket", "Z")); DemuxTlvFilterSettings tlvFilterSettings { .packetType = packetType, .isCompressedIpPacket = isCompressedIpPacket, }; DemuxTlvFilterType tlvType = static_cast(subtype); if (tlvType == DemuxTlvFilterType::SECTION && settingsObj != nullptr) { tlvFilterSettings.filterSettings .set( getFilterSectionSettings(env, settingsObj)); } else if (tlvType == DemuxTlvFilterType::TLV) { bool bPassthrough = env->GetBooleanField(filterConfigObj, env->GetFieldID(clazz, "mPassthrough", "Z")); tlvFilterSettings.filterSettings .set(bPassthrough); } filterSettings.set(tlvFilterSettings); break; } case DemuxFilterMainType::ALP: { jclass clazz = env->FindClass("android/media/tv/tuner/filter/AlpFilterConfiguration"); int32_t packetType = 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, }; if (settingsObj != nullptr) { DemuxAlpFilterType alpType = static_cast(subtype); switch (alpType) { case DemuxAlpFilterType::SECTION: alpFilterSettings.filterSettings .set( getFilterSectionSettings(env, settingsObj)); break; default: break; } } filterSettings.set(alpFilterSettings); break; } default: { break; } } return filterSettings; } static Result configureIpFilterContextId( JNIEnv *env, sp filterClient, jobject ipFilterConfigObj) { jclass clazz = env->FindClass( "android/media/tv/tuner/filter/IpFilterConfiguration"); uint32_t cid = env->GetIntField(ipFilterConfigObj, env->GetFieldID( clazz, "mIpFilterContextId", "I")); return filterClient->configureIpFilterContextId(cid); } static bool isAvFilterSettings(DemuxFilterSettings filterSettings) { return (filterSettings.getTag() == DemuxFilterSettings::Tag::ts && filterSettings.get().filterSettings.getTag() == DemuxTsFilterSettingsFilterSettings::Tag::av) || (filterSettings.getTag() == DemuxFilterSettings::Tag::mmtp && filterSettings.get().filterSettings.getTag() == DemuxMmtpFilterSettingsFilterSettings::Tag::av); } static jint android_media_tv_Tuner_configure_filter( JNIEnv *env, jobject filter, int type, int subtype, jobject settings) { ALOGV("configure filter type=%d, subtype=%d", type, subtype); sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { ALOGD("Failed to configure filter: filter not found"); return (jint)Result::NOT_INITIALIZED; } DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings); Result res = filterClient->configure(filterSettings); if (res != Result::SUCCESS) { return (jint)res; } if (static_cast(type) == DemuxFilterMainType::IP) { res = configureIpFilterContextId(env, filterClient, settings); if (res != Result::SUCCESS) { return (jint)res; } } AvStreamType streamType; if (isAvFilterSettings(filterSettings) && getAvStreamType(env, settings, streamType)) { res = filterClient->configureAvStreamType(streamType); } return (jint)res; } static jint android_media_tv_Tuner_get_filter_id(JNIEnv* env, jobject filter) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { ALOGD("Failed to get filter ID: filter client not found"); return (int) Result::NOT_INITIALIZED; } int32_t id; Result res = filterClient->getId(id); if (res != Result::SUCCESS) { return (jint)Constant::INVALID_FILTER_ID; } return (jint)id; } static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject filter) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { ALOGD("Failed to get filter ID 64 bit: filter client not found"); return (int)Result::NOT_INITIALIZED; } int64_t id; Result res = filterClient->getId64Bit(id); return (res == Result::SUCCESS) ? id : static_cast(Constant64Bit::INVALID_FILTER_ID_64BIT); } static jint android_media_tv_Tuner_configure_monitor_event( JNIEnv* env, jobject filter, int monitorEventType) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { ALOGD("Failed to configure scrambling event: filter client not found"); return (int)Result::NOT_INITIALIZED; } Result res = filterClient->configureMonitorEvent(monitorEventType); return (jint)res; } static jint android_media_tv_Tuner_set_filter_data_source( JNIEnv* env, jobject filter, jobject srcFilter) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { ALOGD("Failed to set filter data source: filter client not found"); return (int)Result::NOT_INITIALIZED; } Result res; if (srcFilter == nullptr) { res = filterClient->setDataSource(nullptr); } else { sp srcClient = getFilterClient(env, srcFilter); if (srcClient == nullptr) { ALOGD("Failed to set filter data source: src filter not found"); return (jint)Result::INVALID_ARGUMENT; } res = filterClient->setDataSource(srcClient); } return (jint)res; } static jint android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) { sp filterClient = nullptr; if (env->IsInstanceOf(filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"))) { filterClient = getSharedFilterClient(env, filter); } else { filterClient = getFilterClient(env, filter); } if (filterClient == nullptr) { ALOGD("Failed to start filter: filter client not found"); return (int)Result::NOT_INITIALIZED; } return (jint)filterClient->start(); } static jint android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) { sp filterClient = nullptr; if (env->IsInstanceOf(filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"))) { filterClient = getSharedFilterClient(env, filter); } else { filterClient = getFilterClient(env, filter); } if (filterClient == nullptr) { ALOGD("Failed to stop filter: filter client not found"); return (int)Result::NOT_INITIALIZED; } return (jint)filterClient->stop(); } static jint android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) { sp filterClient = nullptr; if (env->IsInstanceOf(filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"))) { filterClient = getSharedFilterClient(env, filter); } else { filterClient = getFilterClient(env, filter); } if (filterClient == nullptr) { ALOGD("Failed to flush filter: filter client not found"); return (jint)Result::NOT_INITIALIZED; } return (jint)filterClient->flush(); } static jint android_media_tv_Tuner_read_filter_fmq( JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) { sp filterClient = nullptr; if (env->IsInstanceOf(filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"))) { filterClient = getSharedFilterClient(env, filter); } else { filterClient = getFilterClient(env, filter); } if (filterClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to read filter FMQ: filter client not found"); return -1; } jboolean isCopy; jbyte *dst = env->GetByteArrayElements(buffer, &isCopy); ALOGV("copyData, isCopy=%d", isCopy); if (dst == nullptr) { jniThrowRuntimeException(env, "Failed to GetByteArrayElements"); return -1; } int realReadSize = filterClient->read(reinterpret_cast(dst) + offset, size); env->ReleaseByteArrayElements(buffer, dst, 0); return (jint)realReadSize; } static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) { sp filterClient = nullptr; bool shared = env->IsInstanceOf( filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter")); if (shared) { filterClient = getSharedFilterClient(env, filter); } else { filterClient = getFilterClient(env, filter); } if (filterClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to close filter: filter client not found"); return 0; } Result r = filterClient->close(); filterClient->decStrong(filter); filterClient = nullptr; if (shared) { env->SetLongField(filter, gFields.sharedFilterContext, 0); } else { env->SetLongField(filter, gFields.filterContext, 0); } return (jint)r; } static jstring android_media_tv_Tuner_acquire_shared_filter_token(JNIEnv *env, jobject filter) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to acquire shared filter token: filter client not found"); return nullptr; } string token = filterClient->acquireSharedFilterToken(); if (token.empty()) { return nullptr; } return env->NewStringUTF(token.data()); } static void android_media_tv_Tuner_free_shared_filter_token( JNIEnv *env, jobject filter, jstring token) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to release shared filter: filter client not found"); return; } std::string filterToken(env->GetStringUTFChars(token, nullptr)); filterClient->freeSharedFilterToken(filterToken); } static jint android_media_tv_Tuner_set_filter_time_delay_hint( JNIEnv *env, jobject filter, int timeDelayInMs) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to set filter delay: filter client not found"); } FilterDelayHint delayHint { .hintType = FilterDelayHintType::TIME_DELAY_IN_MS, .hintValue = timeDelayInMs, }; return static_cast(filterClient->setDelayHint(delayHint)); } static jint android_media_tv_Tuner_set_filter_data_size_delay_hint( JNIEnv *env, jobject filter, int dataSizeDelayInBytes) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to set filter delay: filter client not found"); } FilterDelayHint delayHint { .hintType = FilterDelayHintType::DATA_SIZE_DELAY_IN_BYTES, .hintValue = dataSizeDelayInBytes, }; return static_cast(filterClient->setDelayHint(delayHint)); } static sp getTimeFilterClient(JNIEnv *env, jobject filter) { return (TimeFilterClient *)env->GetLongField(filter, gFields.timeFilterContext); } static int android_media_tv_Tuner_time_filter_set_timestamp( JNIEnv *env, jobject filter, jlong timestamp) { sp timeFilterClient = getTimeFilterClient(env, filter); if (timeFilterClient == nullptr) { ALOGD("Failed set timestamp: time filter client not found"); return (int) Result::INVALID_STATE; } return (int)timeFilterClient->setTimeStamp(timestamp); } static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv *env, jobject filter) { sp timeFilterClient = getTimeFilterClient(env, filter); if (timeFilterClient == nullptr) { ALOGD("Failed clear timestamp: time filter client not found"); return (int) Result::INVALID_STATE; } return (int)timeFilterClient->clearTimeStamp(); } static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, jobject filter) { sp timeFilterClient = getTimeFilterClient(env, filter); if (timeFilterClient == nullptr) { ALOGD("Failed get timestamp: time filter client not found"); return nullptr; } int64_t timestamp = timeFilterClient->getTimeStamp(); if (timestamp == (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP) { return nullptr; } jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); jobject longObj = env->NewObject(longClazz, longInit, timestamp); return longObj; } static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, jobject filter) { sp timeFilterClient = getTimeFilterClient(env, filter); if (timeFilterClient == nullptr) { ALOGD("Failed get source time: time filter client not found"); return nullptr; } int64_t timestamp = timeFilterClient->getSourceTime(); if (timestamp == (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP) { return nullptr; } jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); jobject longObj = env->NewObject(longClazz, longInit, timestamp); return longObj; } static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter) { sp timeFilterClient = getTimeFilterClient(env, filter); if (timeFilterClient == nullptr) { ALOGD("Failed close time filter: time filter client not found"); return (int) Result::INVALID_STATE; } Result r = timeFilterClient->close(); timeFilterClient->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 descramblerClient = getDescramblerClient(env, descrambler); if (descramblerClient == nullptr) { return (jint)Result::NOT_INITIALIZED; } sp filterClient = (filter == nullptr) ? nullptr : getFilterClient(env, filter); Result result = descramblerClient->addPid(getDemuxPid((int)pidType, (int)pid), filterClient); return (jint)result; } static jint android_media_tv_Tuner_descrambler_remove_pid( JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) { sp descramblerClient = getDescramblerClient(env, descrambler); if (descramblerClient == nullptr) { return (jint)Result::NOT_INITIALIZED; } sp filterClient = (filter == nullptr) ? nullptr : getFilterClient(env, filter); Result result = descramblerClient->removePid(getDemuxPid((int)pidType, (int)pid), filterClient); return (jint)result; } static jint android_media_tv_Tuner_descrambler_set_key_token( JNIEnv* env, jobject descrambler, jbyteArray keyToken) { sp descramblerClient = getDescramblerClient(env, descrambler); if (descramblerClient == nullptr) { 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 = descramblerClient->setKeyToken(v); return (jint)result; } static jint android_media_tv_Tuner_close_descrambler(JNIEnv* env, jobject descrambler) { sp descramblerClient = getDescramblerClient(env, descrambler); if (descramblerClient == nullptr) { return (jint)Result::NOT_INITIALIZED; } Result r = descramblerClient->close(); descramblerClient->decStrong(descrambler); env->SetLongField(descrambler, gFields.descramblerContext, 0); 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 jobject android_media_tv_Tuner_get_demux_info(JNIEnv* env, jobject thiz, jint handle) { sp tuner = getTuner(env, thiz); return tuner->getDemuxInfo(handle); } static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint handle) { sp tuner = getTuner(env, thiz); return (jint)tuner->openDemux(handle); } static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) { sp tuner = getTuner(env, thiz); setTuner(env, thiz, nullptr); 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 jobject android_media_tv_Tuner_open_shared_filter( JNIEnv* env, jobject /* thiz */, jstring token) { sp tunerClient = new TunerClient(); std::string filterToken(env->GetStringUTFChars(token, nullptr)); sp filterClient; sp callback = new FilterClientCallbackImpl(); filterClient = tunerClient->openSharedFilter(filterToken, callback); if (filterClient == nullptr) { ALOGD("Failed to open shared filter %s", filterToken.c_str()); return nullptr; } jobject filterObj = env->NewObject(env->FindClass("android/media/tv/tuner/filter/SharedFilter"), gFields.sharedFilterInitID); filterClient->incStrong(filterObj); env->SetLongField(filterObj, gFields.sharedFilterContext, (jlong)filterClient.get()); callback->setSharedFilter(env->NewWeakGlobalRef(filterObj), filterClient); return filterObj; } static jstring android_media_tv_Tuner_get_frontend_hardware_info(JNIEnv *env, jobject thiz) { sp tuner = getTuner(env, thiz); string info; Result r = tuner->getFrontendHardwareInfo(info); if (r != Result::SUCCESS) { return nullptr; } return env->NewStringUTF(info.data()); } static jint android_media_tv_Tuner_set_maximum_frontends(JNIEnv *env, jobject thiz, jint type, jint maxNumber) { sp tuner = getTuner(env, thiz); return tuner->setMaxNumberOfFrontends(type, maxNumber); } static jint android_media_tv_Tuner_get_maximum_frontends(JNIEnv *env, jobject thiz, jint type) { sp tuner = getTuner(env, thiz); return tuner->getMaxNumberOfFrontends(type); } static jint android_media_tv_Tuner_remove_output_pid(JNIEnv *env, jobject thiz, jint pid) { sp tuner = getTuner(env, thiz); return tuner->removeOutputPid(pid); } static jobjectArray android_media_tv_Tuner_get_frontend_status_readiness(JNIEnv *env, jobject thiz, jintArray types) { sp tuner = getTuner(env, thiz); return tuner->getFrontendStatusReadiness(types); } 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 filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { return (jint)Result::INVALID_ARGUMENT; } sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { return (jint)Result::NOT_INITIALIZED; } Result result = dvrClient->attachFilter(filterClient); return (jint)result; } static jint android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) { sp filterClient = getFilterClient(env, filter); if (filterClient == nullptr) { return (jint)Result::INVALID_ARGUMENT; } sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { return (jint)Result::NOT_INITIALIZED; } Result result = dvrClient->detachFilter(filterClient); return (jint)result; } static jint android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGD("Failed to configure dvr: dvr client not found"); return (int)Result::NOT_INITIALIZED; } bool isRecorder = env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder")); Result result = dvrClient->configure(getDvrSettings(env, settings, isRecorder)); return (jint)result; } static jint android_media_tv_Tuner_set_status_check_interval_hint(JNIEnv *env, jobject dvr, jlong durationInMs) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGD("Failed to set status check interval hint: dvr client not found"); return (int)Result::NOT_INITIALIZED; } Result result = dvrClient->setStatusCheckIntervalHint(durationInMs); return (jint)result; } static jint android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGD("Failed to start dvr: dvr client not found"); return (jint)Result::NOT_INITIALIZED; } Result result = dvrClient->start(); return (jint)result; } static jint android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGD("Failed to stop dvr: dvr client not found"); return (jint)Result::NOT_INITIALIZED; } Result result = dvrClient->stop(); return (jint)result; } static jint android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGD("Failed to flush dvr: dvr client not found"); return (jint)Result::NOT_INITIALIZED; } Result result = dvrClient->flush(); return (jint)result; } static jint android_media_tv_Tuner_close_dvr(JNIEnv* env, jobject dvr) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGD("Failed to close dvr: dvr client not found"); return (jint)Result::NOT_INITIALIZED; } Result r = dvrClient->close(); bool isRecorder = env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder")); jfieldID fieldId = isRecorder ? gFields.dvrRecorderContext : gFields.dvrPlaybackContext; dvrClient->decStrong(dvr); env->SetLongField(dvr, fieldId, 0); return (jint)r; } static jint android_media_tv_Tuner_lnb_set_voltage(JNIEnv* env, jobject lnb, jint voltage) { sp lnbClient = getLnbClient(env, lnb); return (jint)lnbClient->setVoltage(static_cast(voltage)); } static int android_media_tv_Tuner_lnb_set_tone(JNIEnv* env, jobject lnb, jint tone) { sp lnbClient = getLnbClient(env, lnb); return (jint)lnbClient->setTone(static_cast(tone)); } static int android_media_tv_Tuner_lnb_set_position(JNIEnv* env, jobject lnb, jint position) { sp lnbClient = getLnbClient(env, lnb); return (jint)lnbClient->setSatellitePosition(static_cast(position)); } static int android_media_tv_Tuner_lnb_send_diseqc_msg(JNIEnv* env, jobject lnb, jbyteArray msg) { sp lnbClient = getLnbClient(env, lnb); int size = env->GetArrayLength(msg); std::vector v(size); env->GetByteArrayRegion(msg, 0, size, reinterpret_cast(&v[0])); return (jint)lnbClient->sendDiseqcMessage(v); } static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) { sp lnbClient = getLnbClient(env, lnb); Result r = lnbClient->close(); lnbClient->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 dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGD("Failed to set FD for dvr: dvr client not found"); return; } dvrClient->setFd(fd); ALOGV("set fd = %d", fd); } static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong size) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to read dvr: dvr client not found"); return -1; } return (jlong)dvrClient->readFromFile(size); } static jlong android_media_tv_Tuner_seek_dvr(JNIEnv *env, jobject dvr, jlong pos) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to seek dvr: dvr client not found"); return -1; } return (jlong)dvrClient->seekFile(pos); } static jlong android_media_tv_Tuner_read_dvr_from_array( JNIEnv* env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGW("Failed to read dvr: dvr client not found"); return -1; } jboolean isCopy; jbyte *src = env->GetByteArrayElements(buffer, &isCopy); if (src == nullptr) { ALOGD("Failed to GetByteArrayElements"); return -1; } int64_t realSize = dvrClient->readFromBuffer(reinterpret_cast(src) + offset, size); env->ReleaseByteArrayElements(buffer, src, 0); return (jlong)realSize; } static jlong android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jlong size) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to write dvr: dvr client not found"); return -1; } return (jlong)dvrClient->writeToFile(size); } static jlong android_media_tv_Tuner_write_dvr_to_array( JNIEnv *env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) { sp dvrClient = getDvrClient(env, dvr); if (dvrClient == nullptr) { ALOGW("Failed to read dvr: dvr client not found"); return -1; } jboolean isCopy; jbyte *dst = env->GetByteArrayElements(buffer, &isCopy); ALOGV("copyData, isCopy=%d", isCopy); if (dst == nullptr) { jniThrowRuntimeException(env, "Failed to GetByteArrayElements"); return -1; } int64_t realSize = dvrClient->writeToBuffer(reinterpret_cast(dst) + offset, size); env->ReleaseByteArrayElements(buffer, dst, 0); return (jlong)realSize; } 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 == nullptr) { ALOGD("Failed get MediaEvent"); return nullptr; } android::Mutex::Autolock autoLock(mediaEventSp->mLock); 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 == nullptr) { ALOGD("Failed get MediaEvent"); return nullptr; } android::Mutex::Autolock autoLock(mediaEventSp->mLock); int64_t audioHandle = mediaEventSp->getAudioHandle(); jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); jobject longObj = env->NewObject(longClazz, longInit, audioHandle); return longObj; } static void android_media_tv_Tuner_media_event_finalize(JNIEnv* env, jobject mediaEventObj) { sp mediaEventSp = getMediaEventSp(env, mediaEventObj); if (mediaEventSp == nullptr) { 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 }, { "nativeGetTunerVersion", "()I", (void *)android_media_tv_Tuner_native_get_tuner_version }, { "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 }, { "nativeShareFrontend", "(I)I", (void *)android_media_tv_Tuner_share_frontend }, { "nativeUnshareFrontend", "()I", (void *)android_media_tv_Tuner_unshare_frontend }, { "nativeRegisterFeCbListener", "(J)V", (void*)android_media_tv_Tuner_register_fe_cb_listener }, { "nativeUnregisterFeCbListener", "(J)V", (void*)android_media_tv_Tuner_unregister_fe_cb_listener }, { "nativeUpdateFrontend", "(J)V", (void*)android_media_tv_Tuner_update_frontend }, { "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", "(Landroid/media/tv/tuner/Lnb;)I", (void *)android_media_tv_Tuner_set_lnb }, { "nativeIsLnaSupported", "()Z", (void *)android_media_tv_Tuner_is_lna_supported }, { "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 }, { "nativeLinkCiCam", "(I)I", (void *)android_media_tv_Tuner_link_cicam }, { "nativeUnlinkCiCam", "(I)I", (void *)android_media_tv_Tuner_unlink_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 }, { "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 }, { "nativeGetDemuxInfo", "(I)Landroid/media/tv/tuner/DemuxInfo;", (void *)android_media_tv_Tuner_get_demux_info }, { "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 }, { "nativeOpenSharedFilter", "(Ljava/lang/String;)Landroid/media/tv/tuner/filter/SharedFilter;", (void *)android_media_tv_Tuner_open_shared_filter}, { "nativeGetFrontendHardwareInfo","()Ljava/lang/String;", (void *)android_media_tv_Tuner_get_frontend_hardware_info }, { "nativeSetMaxNumberOfFrontends", "(II)I", (void *)android_media_tv_Tuner_set_maximum_frontends }, { "nativeGetMaxNumberOfFrontends", "(I)I", (void *)android_media_tv_Tuner_get_maximum_frontends }, { "nativeRemoveOutputPid", "(I)I", (void *)android_media_tv_Tuner_remove_output_pid }, { "nativeGetFrontendStatusReadiness", "([I)[Landroid/media/tv/tuner/frontend/FrontendStatusReadiness;", (void *)android_media_tv_Tuner_get_frontend_status_readiness }, }; 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}, { "nativeGetId64Bit", "()J", (void *)android_media_tv_Tuner_get_filter_64bit_id}, { "nativeConfigureMonitorEvent", "(I)I", (void *)android_media_tv_Tuner_configure_monitor_event}, { "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}, { "nativeAcquireSharedFilterToken", "()Ljava/lang/String;", (void *)android_media_tv_Tuner_acquire_shared_filter_token}, { "nativeFreeSharedFilterToken", "(Ljava/lang/String;)V", (void *)android_media_tv_Tuner_free_shared_filter_token}, {"nativeSetTimeDelayHint", "(I)I", (void *)android_media_tv_Tuner_set_filter_time_delay_hint}, {"nativeSetDataSizeDelayHint", "(I)I", (void *)android_media_tv_Tuner_set_filter_data_size_delay_hint}, }; static const JNINativeMethod gSharedFilterMethods[] = { { "nativeStartSharedFilter", "()I", (void *)android_media_tv_Tuner_start_filter}, { "nativeStopSharedFilter", "()I", (void *)android_media_tv_Tuner_stop_filter}, { "nativeFlushSharedFilter", "()I", (void *)android_media_tv_Tuner_flush_filter}, { "nativeSharedRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq}, { "nativeSharedClose", "()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 }, { "nativeSetStatusCheckIntervalHint", "(J)I", (void *)android_media_tv_Tuner_set_status_check_interval_hint}, { "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}, { "nativeSetStatusCheckIntervalHint", "(J)I", (void *)android_media_tv_Tuner_set_status_check_interval_hint}, { "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}, { "nativeSeek", "(J)J", (void *)android_media_tv_Tuner_seek_dvr}, }; 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/SharedFilter", gSharedFilterMethods, NELEM(gSharedFilterMethods)) != JNI_OK) { ALOGE("Failed to register shared 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 = nullptr; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { ALOGE("ERROR: GetEnv failed"); return result; } assert(env != nullptr); if (!register_android_media_tv_Tuner(env)) { ALOGE("ERROR: Tuner native registration failed"); return result; } return JNI_VERSION_1_4; }