/* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "DvrClient" #include #include #include #include "ClientHelper.h" #include "DvrClient.h" using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits; using ::android::hardware::tv::tuner::V1_0::Result; namespace android { /////////////// DvrClient /////////////////////// DvrClient::DvrClient(shared_ptr tunerDvr) { mTunerDvr = tunerDvr; mFd = -1; mDvrMQ = NULL; mDvrMQEventFlag = NULL; } DvrClient::~DvrClient() { mTunerDvr = NULL; mDvr = NULL; mFd = -1; mDvrMQ = NULL; mDvrMQEventFlag = NULL; } // TODO: remove after migration to Tuner Service is done. void DvrClient::setHidlDvr(sp dvr) { mDvr = dvr; } void DvrClient::setFd(int fd) { mFd = fd; } long DvrClient::readFromFile(long size) { if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) { ALOGE("Failed to readFromFile. DVR mq is not configured"); return -1; } if (mFd < 0) { ALOGE("Failed to readFromFile. File is not configured"); return -1; } long available = mDvrMQ->availableToWrite(); long write = min(size, available); AidlMQ::MemTransaction tx; long ret = 0; if (mDvrMQ->beginWrite(write, &tx)) { auto first = tx.getFirstRegion(); auto data = first.getAddress(); long length = first.getLength(); long firstToWrite = min(length, write); ret = read(mFd, data, firstToWrite); if (ret < 0) { ALOGE("Failed to read from FD: %s", strerror(errno)); return -1; } if (ret < firstToWrite) { ALOGW("file to MQ, first region: %ld bytes to write, but %ld bytes written", firstToWrite, ret); } else if (firstToWrite < write) { ALOGD("write second region: %ld bytes written, %ld bytes in total", ret, write); auto second = tx.getSecondRegion(); data = second.getAddress(); length = second.getLength(); int secondToWrite = std::min(length, write - firstToWrite); ret += read(mFd, data, secondToWrite); } ALOGD("file to MQ: %ld bytes need to be written, %ld bytes written", write, ret); if (!mDvrMQ->commitWrite(ret)) { ALOGE("Error: failed to commit write!"); return -1; } } else { ALOGE("dvrMq.beginWrite failed"); } if (ret > 0) { mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_READY)); } return ret; } long DvrClient::readFromBuffer(int8_t* buffer, long size) { if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) { ALOGE("Failed to readFromBuffer. DVR mq is not configured"); return -1; } if (buffer == nullptr) { ALOGE("Failed to readFromBuffer. Buffer can't be null"); return -1; } long available = mDvrMQ->availableToWrite(); size = min(size, available); if (mDvrMQ->write(buffer, size)) { mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_READY)); } else { ALOGD("Failed to write FMQ"); return -1; } return size; } long DvrClient::writeToFile(long size) { if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) { ALOGE("Failed to writeToFile. DVR mq is not configured"); return -1; } if (mFd < 0) { ALOGE("Failed to writeToFile. File is not configured"); return -1; } long available = mDvrMQ->availableToRead(); long toRead = min(size, available); long ret = 0; AidlMQ::MemTransaction tx; if (mDvrMQ->beginRead(toRead, &tx)) { auto first = tx.getFirstRegion(); auto data = first.getAddress(); long length = first.getLength(); long firstToRead = std::min(length, toRead); ret = write(mFd, data, firstToRead); if (ret < 0) { ALOGE("Failed to write to FD: %s", strerror(errno)); return -1; } if (ret < firstToRead) { ALOGW("MQ to file: %ld bytes read, but %ld bytes written", firstToRead, ret); } else if (firstToRead < toRead) { ALOGD("read second region: %ld bytes read, %ld bytes in total", ret, toRead); auto second = tx.getSecondRegion(); data = second.getAddress(); length = second.getLength(); int secondToRead = toRead - firstToRead; ret += write(mFd, data, secondToRead); } ALOGD("MQ to file: %ld bytes to be read, %ld bytes written", toRead, ret); if (!mDvrMQ->commitRead(ret)) { ALOGE("Error: failed to commit read!"); return 0; } } else { ALOGE("dvrMq.beginRead failed"); } if (ret > 0) { mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_CONSUMED)); } return ret; } long DvrClient::writeToBuffer(int8_t* buffer, long size) { if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) { ALOGE("Failed to writetoBuffer. DVR mq is not configured"); return -1; } if (buffer == nullptr) { ALOGE("Failed to writetoBuffer. Buffer can't be null"); return -1; } long available = mDvrMQ->availableToRead(); size = min(size, available); if (mDvrMQ->read(buffer, size)) { mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_CONSUMED)); } else { ALOGD("Failed to write FMQ"); return -1; } return size; } Result DvrClient::configure(DvrSettings settings) { if (mTunerDvr != NULL) { TunerDvrSettings dvrSettings = getAidlDvrSettingsFromHidl(settings); Status s = mTunerDvr->configure(dvrSettings); Result res = ClientHelper::getServiceSpecificErrorCode(s); if (res != Result::SUCCESS) { return res; } AidlMQDesc aidlMqDesc; s = mTunerDvr->getQueueDesc(&aidlMqDesc); res = ClientHelper::getServiceSpecificErrorCode(s); if (res != Result::SUCCESS) { return res; } mDvrMQ = new (nothrow) AidlMQ(aidlMqDesc); EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag); return res; } if (mDvr != NULL) { Result res = mDvr->configure(settings); if (res == Result::SUCCESS) { MQDescriptorSync dvrMQDesc; res = getQueueDesc(dvrMQDesc); if (res == Result::SUCCESS) { AidlMQDesc aidlMQDesc; unsafeHidlToAidlMQDescriptor( dvrMQDesc, &aidlMQDesc); mDvrMQ = new (nothrow) AidlMessageQueue(aidlMQDesc); EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag); } } return res; } return Result::INVALID_STATE; } Result DvrClient::attachFilter(sp filterClient) { if (mTunerDvr != NULL) { Status s = mTunerDvr->attachFilter(filterClient->getAidlFilter()); return ClientHelper::getServiceSpecificErrorCode(s); } if (mDvr != NULL) { sp hidlFilter = filterClient->getHalFilter(); if (hidlFilter == NULL) { return Result::INVALID_ARGUMENT; } return mDvr->attachFilter(hidlFilter); } return Result::INVALID_STATE; } Result DvrClient::detachFilter(sp filterClient) { if (mTunerDvr != NULL) { Status s = mTunerDvr->detachFilter(filterClient->getAidlFilter()); return ClientHelper::getServiceSpecificErrorCode(s); } if (mDvr != NULL) { sp hidlFilter = filterClient->getHalFilter(); if (hidlFilter == NULL) { return Result::INVALID_ARGUMENT; } return mDvr->detachFilter(hidlFilter); } return Result::INVALID_STATE; } Result DvrClient::start() { if (mTunerDvr != NULL) { Status s = mTunerDvr->start(); return ClientHelper::getServiceSpecificErrorCode(s); } if (mDvr != NULL) { return mDvr->start(); } return Result::INVALID_STATE; } Result DvrClient::stop() { if (mTunerDvr != NULL) { Status s = mTunerDvr->stop(); return ClientHelper::getServiceSpecificErrorCode(s); } if (mDvr != NULL) { return mDvr->stop(); } return Result::INVALID_STATE; } Result DvrClient::flush() { if (mTunerDvr != NULL) { Status s = mTunerDvr->flush(); return ClientHelper::getServiceSpecificErrorCode(s); } if (mDvr != NULL) { return mDvr->flush(); } return Result::INVALID_STATE; } Result DvrClient::close() { if (mDvrMQEventFlag != NULL) { EventFlag::deleteEventFlag(&mDvrMQEventFlag); } mDvrMQ = NULL; if (mTunerDvr != NULL) { Status s = mTunerDvr->close(); mTunerDvr = NULL; return ClientHelper::getServiceSpecificErrorCode(s); } if (mDvr != NULL) { Result res = mDvr->close(); mDvr = NULL; return res; } return Result::INVALID_STATE; } /////////////// IDvrCallback /////////////////////// HidlDvrCallback::HidlDvrCallback(sp dvrClientCallback) : mDvrClientCallback(dvrClientCallback) {} Return HidlDvrCallback::onRecordStatus(const RecordStatus status) { if (mDvrClientCallback != NULL) { mDvrClientCallback->onRecordStatus(status); } return Void(); } Return HidlDvrCallback::onPlaybackStatus(const PlaybackStatus status) { if (mDvrClientCallback != NULL) { mDvrClientCallback->onPlaybackStatus(status); } return Void(); } /////////////// TunerDvrCallback /////////////////////// TunerDvrCallback::TunerDvrCallback(sp dvrClientCallback) : mDvrClientCallback(dvrClientCallback) {} Status TunerDvrCallback::onRecordStatus(int status) { if (mDvrClientCallback != NULL) { mDvrClientCallback->onRecordStatus(static_cast(status)); return Status::ok(); } return Status::fromServiceSpecificError(static_cast(Result::INVALID_STATE)); } Status TunerDvrCallback::onPlaybackStatus(int status) { if (mDvrClientCallback != NULL) { mDvrClientCallback->onPlaybackStatus(static_cast(status)); return Status::ok(); } return Status::fromServiceSpecificError(static_cast(Result::INVALID_STATE)); } /////////////// DvrClient Helper Methods /////////////////////// Result DvrClient::getQueueDesc(MQDesc& dvrMQDesc) { if (mDvr != NULL) { Result res = Result::UNKNOWN_ERROR; mDvr->getQueueDesc([&](Result r, const MQDesc& desc) { dvrMQDesc = desc; res = r; }); return res; } return Result::INVALID_STATE; } TunerDvrSettings DvrClient::getAidlDvrSettingsFromHidl(DvrSettings settings) { TunerDvrSettings s; switch (settings.getDiscriminator()) { case DvrSettings::hidl_discriminator::record: { s.statusMask = static_cast(settings.record().statusMask); s.lowThreshold = static_cast(settings.record().lowThreshold); s.highThreshold = static_cast(settings.record().highThreshold); s.dataFormat = static_cast(settings.record().dataFormat); s.packetSize = static_cast(settings.record().packetSize); return s; } case DvrSettings::hidl_discriminator::playback: { s.statusMask = static_cast(settings.playback().statusMask); s.lowThreshold = static_cast(settings.playback().lowThreshold); s.highThreshold = static_cast(settings.playback().highThreshold); s.dataFormat = static_cast(settings.playback().dataFormat); s.packetSize = static_cast(settings.playback().packetSize); return s; } default: break; } return s; } } // namespace android