|  | /** | 
|  | * Copyright (c) 2021, The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | //#define LOG_NDEBUG 0 | 
|  | #define LOG_TAG "TunerService" | 
|  |  | 
|  | #include "TunerService.h" | 
|  |  | 
|  | #include <aidl/android/hardware/tv/tuner/IDemux.h> | 
|  | #include <aidl/android/hardware/tv/tuner/IDescrambler.h> | 
|  | #include <aidl/android/hardware/tv/tuner/IFrontend.h> | 
|  | #include <aidl/android/hardware/tv/tuner/ILnb.h> | 
|  | #include <aidl/android/hardware/tv/tuner/Result.h> | 
|  | #include <android/binder_manager.h> | 
|  | #include <binder/IPCThreadState.h> | 
|  | #include <binder/PermissionCache.h> | 
|  | #include <utils/Log.h> | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "TunerDemux.h" | 
|  | #include "TunerDescrambler.h" | 
|  | #include "TunerFrontend.h" | 
|  | #include "TunerHelper.h" | 
|  | #include "TunerLnb.h" | 
|  |  | 
|  | using ::aidl::android::hardware::tv::tuner::IDemux; | 
|  | using ::aidl::android::hardware::tv::tuner::IDescrambler; | 
|  | using ::aidl::android::hardware::tv::tuner::IFrontend; | 
|  | using ::aidl::android::hardware::tv::tuner::Result; | 
|  | using ::android::IPCThreadState; | 
|  | using ::android::PermissionCache; | 
|  | using ::android::sp; | 
|  |  | 
|  | namespace aidl { | 
|  | namespace android { | 
|  | namespace media { | 
|  | namespace tv { | 
|  | namespace tuner { | 
|  |  | 
|  | shared_ptr<TunerService> TunerService::sTunerService = nullptr; | 
|  |  | 
|  | TunerService::TunerService() { | 
|  | if (!TunerHelper::checkTunerFeature()) { | 
|  | ALOGD("Device doesn't have tuner hardware."); | 
|  | return; | 
|  | } | 
|  |  | 
|  | updateTunerResources(); | 
|  | } | 
|  |  | 
|  | TunerService::~TunerService() {} | 
|  |  | 
|  | binder_status_t TunerService::instantiate() { | 
|  | sTunerService = ::ndk::SharedRefBase::make<TunerService>(); | 
|  | return AServiceManager_addService(sTunerService->asBinder().get(), getServiceName()); | 
|  | } | 
|  |  | 
|  | shared_ptr<TunerService> TunerService::getTunerService() { | 
|  | return sTunerService; | 
|  | } | 
|  |  | 
|  | bool TunerService::hasITuner() { | 
|  | ALOGV("hasITuner"); | 
|  | if (mTuner != nullptr) { | 
|  | return true; | 
|  | } | 
|  | const string statsServiceName = string() + ITuner::descriptor + "/default"; | 
|  | if (AServiceManager_isDeclared(statsServiceName.c_str())) { | 
|  | ::ndk::SpAIBinder binder(AServiceManager_waitForService(statsServiceName.c_str())); | 
|  | mTuner = ITuner::fromBinder(binder); | 
|  | } else { | 
|  | mTuner = nullptr; | 
|  | ALOGE("Failed to get Tuner HAL Service"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | mTunerVersion = TUNER_HAL_VERSION_2_0; | 
|  | // TODO: Enable this after Tuner HAL is frozen. | 
|  | // if (mTuner->getInterfaceVersion(&mTunerVersion).isOk()) { | 
|  | //  // Tuner AIDL HAL version 1 will be Tuner HAL 2.0 | 
|  | //  mTunerVersion = (mTunerVersion + 1) << 16; | 
|  | //} | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::openDemux(int32_t /* in_demuxHandle */, | 
|  | shared_ptr<ITunerDemux>* _aidl_return) { | 
|  | ALOGV("openDemux"); | 
|  | if (!hasITuner()) { | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  | vector<int32_t> id; | 
|  | shared_ptr<IDemux> demux; | 
|  | auto status = mTuner->openDemux(&id, &demux); | 
|  | if (status.isOk()) { | 
|  | *_aidl_return = ::ndk::SharedRefBase::make<TunerDemux>(demux, id[0]); | 
|  | } | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::getDemuxCaps(DemuxCapabilities* _aidl_return) { | 
|  | ALOGV("getDemuxCaps"); | 
|  | if (!hasITuner()) { | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | return mTuner->getDemuxCaps(_aidl_return); | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::getFrontendIds(vector<int32_t>* ids) { | 
|  | if (!hasITuner()) { | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | return mTuner->getFrontendIds(ids); | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::getFrontendInfo(int32_t id, FrontendInfo* _aidl_return) { | 
|  | if (!hasITuner()) { | 
|  | ALOGE("ITuner service is not init."); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | return mTuner->getFrontendInfo(id, _aidl_return); | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::openFrontend(int32_t frontendHandle, | 
|  | shared_ptr<ITunerFrontend>* _aidl_return) { | 
|  | if (!hasITuner()) { | 
|  | ALOGE("ITuner service is not init."); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | int id = TunerHelper::getResourceIdFromHandle(frontendHandle, FRONTEND); | 
|  | shared_ptr<IFrontend> frontend; | 
|  | auto status = mTuner->openFrontendById(id, &frontend); | 
|  | if (status.isOk()) { | 
|  | *_aidl_return = ::ndk::SharedRefBase::make<TunerFrontend>(frontend, id); | 
|  | } | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::openLnb(int lnbHandle, shared_ptr<ITunerLnb>* _aidl_return) { | 
|  | if (!hasITuner()) { | 
|  | ALOGD("get ITuner failed"); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | shared_ptr<ILnb> lnb; | 
|  | int id = TunerHelper::getResourceIdFromHandle(lnbHandle, LNB); | 
|  | auto status = mTuner->openLnbById(id, &lnb); | 
|  | if (status.isOk()) { | 
|  | *_aidl_return = ::ndk::SharedRefBase::make<TunerLnb>(lnb, id); | 
|  | } | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::openLnbByName(const string& lnbName, | 
|  | shared_ptr<ITunerLnb>* _aidl_return) { | 
|  | if (!hasITuner()) { | 
|  | ALOGE("get ITuner failed"); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | vector<int32_t> id; | 
|  | shared_ptr<ILnb> lnb; | 
|  | auto status = mTuner->openLnbByName(lnbName, &id, &lnb); | 
|  | if (status.isOk()) { | 
|  | *_aidl_return = ::ndk::SharedRefBase::make<TunerLnb>(lnb, id[0]); | 
|  | } | 
|  |  | 
|  | return ::ndk::ScopedAStatus::ok(); | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::openDescrambler(int32_t /*descramblerHandle*/, | 
|  | shared_ptr<ITunerDescrambler>* _aidl_return) { | 
|  | if (!hasITuner()) { | 
|  | ALOGD("get ITuner failed"); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | shared_ptr<IDescrambler> descrambler; | 
|  | // int id = TunerHelper::getResourceIdFromHandle(descramblerHandle, DESCRAMBLER); | 
|  | auto status = mTuner->openDescrambler(&descrambler); | 
|  | if (status.isOk()) { | 
|  | *_aidl_return = ::ndk::SharedRefBase::make<TunerDescrambler>(descrambler); | 
|  | } | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::getTunerHalVersion(int* _aidl_return) { | 
|  | hasITuner(); | 
|  | *_aidl_return = mTunerVersion; | 
|  | return ::ndk::ScopedAStatus::ok(); | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::openSharedFilter(const string& in_filterToken, | 
|  | const shared_ptr<ITunerFilterCallback>& in_cb, | 
|  | shared_ptr<ITunerFilter>* _aidl_return) { | 
|  | if (!hasITuner()) { | 
|  | ALOGE("get ITuner failed"); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | if (!PermissionCache::checkCallingPermission(sSharedFilterPermission)) { | 
|  | ALOGE("Request requires android.permission.ACCESS_TV_SHARED_FILTER"); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | Mutex::Autolock _l(mSharedFiltersLock); | 
|  | if (mSharedFilters.find(in_filterToken) == mSharedFilters.end()) { | 
|  | *_aidl_return = nullptr; | 
|  | ALOGD("fail to find %s", in_filterToken.c_str()); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::INVALID_STATE)); | 
|  | } | 
|  |  | 
|  | shared_ptr<TunerFilter> filter = mSharedFilters.at(in_filterToken); | 
|  | IPCThreadState* ipc = IPCThreadState::self(); | 
|  | const int pid = ipc->getCallingPid(); | 
|  | if (!filter->isSharedFilterAllowed(pid)) { | 
|  | *_aidl_return = nullptr; | 
|  | ALOGD("shared filter %s is opened in the same process", in_filterToken.c_str()); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::INVALID_STATE)); | 
|  | } | 
|  |  | 
|  | filter->attachSharedFilterCallback(in_cb); | 
|  |  | 
|  | *_aidl_return = filter; | 
|  | return ::ndk::ScopedAStatus::ok(); | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::setLna(bool bEnable) { | 
|  | if (!hasITuner()) { | 
|  | ALOGD("get ITuner failed"); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | return mTuner->setLna(bEnable); | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::setMaxNumberOfFrontends(FrontendType in_frontendType, | 
|  | int32_t in_maxNumber) { | 
|  | if (!hasITuner()) { | 
|  | ALOGD("get ITuner failed"); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | return mTuner->setMaxNumberOfFrontends(in_frontendType, in_maxNumber); | 
|  | } | 
|  |  | 
|  | ::ndk::ScopedAStatus TunerService::getMaxNumberOfFrontends(FrontendType in_frontendType, | 
|  | int32_t* _aidl_return) { | 
|  | if (!hasITuner()) { | 
|  | ALOGD("get ITuner failed"); | 
|  | return ::ndk::ScopedAStatus::fromServiceSpecificError( | 
|  | static_cast<int32_t>(Result::UNAVAILABLE)); | 
|  | } | 
|  |  | 
|  | return mTuner->getMaxNumberOfFrontends(in_frontendType, _aidl_return); | 
|  | } | 
|  |  | 
|  | string TunerService::addFilterToShared(const shared_ptr<TunerFilter>& sharedFilter) { | 
|  | Mutex::Autolock _l(mSharedFiltersLock); | 
|  |  | 
|  | // Use sharedFilter address as token. | 
|  | string token = to_string(reinterpret_cast<std::uintptr_t>(sharedFilter.get())); | 
|  | mSharedFilters[token] = sharedFilter; | 
|  | return token; | 
|  | } | 
|  |  | 
|  | void TunerService::removeSharedFilter(const shared_ptr<TunerFilter>& sharedFilter) { | 
|  | Mutex::Autolock _l(mSharedFiltersLock); | 
|  |  | 
|  | // Use sharedFilter address as token. | 
|  | mSharedFilters.erase(to_string(reinterpret_cast<std::uintptr_t>(sharedFilter.get()))); | 
|  | } | 
|  |  | 
|  | void TunerService::updateTunerResources() { | 
|  | if (!hasITuner()) { | 
|  | ALOGE("Failed to updateTunerResources"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | TunerHelper::updateTunerResources(getTRMFrontendInfos(), getTRMLnbHandles()); | 
|  | } | 
|  |  | 
|  | vector<TunerFrontendInfo> TunerService::getTRMFrontendInfos() { | 
|  | vector<TunerFrontendInfo> infos; | 
|  | vector<int32_t> ids; | 
|  | auto status = mTuner->getFrontendIds(&ids); | 
|  | if (!status.isOk()) { | 
|  | return infos; | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < ids.size(); i++) { | 
|  | FrontendInfo frontendInfo; | 
|  | auto res = mTuner->getFrontendInfo(ids[i], &frontendInfo); | 
|  | if (!res.isOk()) { | 
|  | continue; | 
|  | } | 
|  | TunerFrontendInfo tunerFrontendInfo{ | 
|  | .handle = TunerHelper::getResourceHandleFromId((int)ids[i], FRONTEND), | 
|  | .type = static_cast<int>(frontendInfo.type), | 
|  | .exclusiveGroupId = frontendInfo.exclusiveGroupId, | 
|  | }; | 
|  | infos.push_back(tunerFrontendInfo); | 
|  | } | 
|  |  | 
|  | return infos; | 
|  | } | 
|  |  | 
|  | vector<int32_t> TunerService::getTRMLnbHandles() { | 
|  | vector<int32_t> lnbHandles; | 
|  | if (mTuner != nullptr) { | 
|  | vector<int32_t> lnbIds; | 
|  | auto res = mTuner->getLnbIds(&lnbIds); | 
|  | if (res.isOk()) { | 
|  | for (int i = 0; i < lnbIds.size(); i++) { | 
|  | lnbHandles.push_back(TunerHelper::getResourceHandleFromId(lnbIds[i], LNB)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return lnbHandles; | 
|  | } | 
|  |  | 
|  | }  // namespace tuner | 
|  | }  // namespace tv | 
|  | }  // namespace media | 
|  | }  // namespace android | 
|  | }  // namespace aidl |