Add Tuner AIDL HAL VTS test cases
Bug: 191825295
Test: make and run VtsHalTvTunerTargetTest
Change-Id: Id7360e1b2da148db5c13ed2bdf3c866cc53db17b
diff --git a/tv/tuner/aidl/TEST_MAPPING b/tv/tuner/aidl/TEST_MAPPING
new file mode 100644
index 0000000..222c6ed
--- /dev/null
+++ b/tv/tuner/aidl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "VtsHalTvTunerTargetTest"
+ }
+ ]
+}
diff --git a/tv/tuner/aidl/vts/OWNERS b/tv/tuner/aidl/vts/OWNERS
new file mode 100644
index 0000000..bf2b609
--- /dev/null
+++ b/tv/tuner/aidl/vts/OWNERS
@@ -0,0 +1,3 @@
+hgchen@google.com
+shubang@google.com
+quxiangfang@google.com
diff --git a/tv/tuner/aidl/vts/functional/Android.bp b/tv/tuner/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..119230e
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/Android.bp
@@ -0,0 +1,77 @@
+//
+// Copyright 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.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalTvTunerTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "DemuxTests.cpp",
+ "DescramblerTests.cpp",
+ "DvrTests.cpp",
+ "FilterTests.cpp",
+ "FrontendTests.cpp",
+ "LnbTests.cpp",
+ "VtsHalTvTunerTargetTest.cpp",
+ ],
+ generated_headers: [
+ "tuner_testing_dynamic_configuration_V1_0_enums",
+ "tuner_testing_dynamic_configuration_V1_0_parser",
+ ],
+ generated_sources: [
+ "tuner_testing_dynamic_configuration_V1_0_enums",
+ "tuner_testing_dynamic_configuration_V1_0_parser",
+ ],
+ header_libs: ["libxsdc-utils"],
+ static_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas@1.1",
+ "android.hardware.cas@1.2",
+ "android.hardware.common-V2-ndk_platform",
+ "android.hardware.common.fmq-V1-ndk_platform",
+ "android.hardware.tv.tuner-V1-ndk_platform",
+ "libaidlcommonsupport",
+ "libfmq",
+ "libcutils",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libvndksupport",
+ "libxml2",
+ ],
+ data: [
+ ":tuner_frontend_input_ts",
+ ":tuner_frontend_input_es",
+ ":tuner_testing_dynamic_configuration_V1_0",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+
+ require_root: true,
+}
diff --git a/tv/tuner/aidl/vts/functional/AndroidTest.xml b/tv/tuner/aidl/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..f93ed78
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<configuration description="Runs VtsHalTvTunerTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalTvTunerTargetTest->/data/local/tmp/VtsHalTvTunerTargetTest" />
+ <option name="push" value="test.es->/data/local/tmp/test.es" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalTvTunerTargetTest" />
+ <option name="native-test-timeout" value="30m" />
+ </test>
+</configuration>
diff --git a/tv/tuner/aidl/vts/functional/DemuxTests.cpp b/tv/tuner/aidl/vts/functional/DemuxTests.cpp
new file mode 100644
index 0000000..9de01e1
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/DemuxTests.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 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.
+ */
+
+#include "DemuxTests.h"
+
+AssertionResult DemuxTests::openDemux(std::shared_ptr<IDemux>& demux, int32_t& demuxId) {
+ std::vector<int32_t> id;
+ auto status = mService->openDemux(&id, &mDemux);
+ if (status.isOk()) {
+ demux = mDemux;
+ demuxId = id[0];
+ }
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DemuxTests::setDemuxFrontendDataSource(int32_t frontendId) {
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ auto status = mDemux->setFrontendDataSource(frontendId);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DemuxTests::getDemuxCaps(DemuxCapabilities& demuxCaps) {
+ if (!mDemux) {
+ ALOGW("[vts] Test with openDemux first.");
+ return failure();
+ }
+ auto status = mService->getDemuxCaps(&demuxCaps);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DemuxTests::getAvSyncId(std::shared_ptr<IFilter> filter, int32_t& avSyncHwId) {
+ EXPECT_TRUE(mDemux) << "Demux is not opened yet.";
+
+ auto status = mDemux->getAvSyncHwId(filter, &avSyncHwId);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DemuxTests::getAvSyncTime(int32_t avSyncId) {
+ EXPECT_TRUE(mDemux) << "Demux is not opened yet.";
+
+ int64_t syncTime;
+ auto status = mDemux->getAvSyncTime(avSyncId, &syncTime);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DemuxTests::closeDemux() {
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ auto status = mDemux->close();
+ mDemux = nullptr;
+ return AssertionResult(status.isOk());
+}
diff --git a/tv/tuner/aidl/vts/functional/DemuxTests.h b/tv/tuner/aidl/vts/functional/DemuxTests.h
new file mode 100644
index 0000000..7698de3
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/DemuxTests.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/tv/tuner/DemuxCapabilities.h>
+#include <aidl/android/hardware/tv/tuner/IDemux.h>
+#include <aidl/android/hardware/tv/tuner/IFilter.h>
+#include <aidl/android/hardware/tv/tuner/ITuner.h>
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+using ::testing::AssertionResult;
+
+using namespace aidl::android::hardware::tv::tuner;
+
+class DemuxTests {
+ public:
+ void setService(std::shared_ptr<ITuner> tuner) { mService = tuner; }
+
+ AssertionResult openDemux(std::shared_ptr<IDemux>& demux, int32_t& demuxId);
+ AssertionResult setDemuxFrontendDataSource(int32_t frontendId);
+ AssertionResult getAvSyncId(std::shared_ptr<IFilter> filter, int32_t& avSyncHwId);
+ AssertionResult getAvSyncTime(int32_t avSyncId);
+ AssertionResult getDemuxCaps(DemuxCapabilities& demuxCaps);
+ AssertionResult closeDemux();
+
+ protected:
+ static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+ static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+ std::shared_ptr<ITuner> mService;
+ std::shared_ptr<IDemux> mDemux;
+};
diff --git a/tv/tuner/aidl/vts/functional/DescramblerTests.cpp b/tv/tuner/aidl/vts/functional/DescramblerTests.cpp
new file mode 100644
index 0000000..e0ee391
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/DescramblerTests.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright 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.
+ */
+
+#include "DescramblerTests.h"
+
+using namespace std;
+
+AssertionResult DescramblerTests::createCasPlugin(int32_t caSystemId) {
+ auto status = mMediaCasService->isSystemIdSupported(caSystemId);
+ if (!status.isOk() || !status) {
+ ALOGW("[vts] Failed to check isSystemIdSupported.");
+ return failure();
+ }
+
+ mCasListener = new MediaCasListener();
+ auto pluginStatus = mMediaCasService->createPluginExt(caSystemId, mCasListener);
+ if (!pluginStatus.isOk()) {
+ ALOGW("[vts] Failed to createPluginExt.");
+ return failure();
+ }
+ mCas = ICas::castFrom(pluginStatus);
+ if (mCas == nullptr) {
+ ALOGW("[vts] Failed to get ICas.");
+ return failure();
+ }
+ return success();
+}
+
+AssertionResult DescramblerTests::openCasSession(vector<uint8_t>& sessionId,
+ vector<uint8_t>& hidlPvtData) {
+ Status sessionStatus;
+ SessionIntent intent = SessionIntent::LIVE;
+ ScramblingMode mode = ScramblingMode::RESERVED;
+ auto returnVoid =
+ mCas->openSession_1_2(intent, mode, [&](Status status, const hidl_vec<uint8_t>& id) {
+ sessionStatus = status;
+ sessionId = id;
+ });
+ if (!returnVoid.isOk() || (sessionStatus != Status::OK)) {
+ ALOGW("[vts] Failed to open cas session.");
+ mCas->closeSession(sessionId);
+ return failure();
+ }
+
+ if (hidlPvtData.size() > 0) {
+ auto status = mCas->setSessionPrivateData(sessionId, hidlPvtData);
+ if (status != android::hardware::cas::V1_0::Status::OK) {
+ ALOGW("[vts] Failed to set session private data");
+ mCas->closeSession(sessionId);
+ return failure();
+ }
+ }
+
+ return success();
+}
+
+AssertionResult DescramblerTests::getKeyToken(int32_t caSystemId, string& provisonStr,
+ vector<uint8_t>& hidlPvtData,
+ vector<uint8_t>& token) {
+ if (createCasPlugin(caSystemId) != success()) {
+ ALOGW("[vts] createCasPlugin failed.");
+ return failure();
+ }
+
+ if (provisonStr.size() > 0) {
+ auto returnStatus = mCas->provision(hidl_string(provisonStr));
+ if (returnStatus != android::hardware::cas::V1_0::Status::OK) {
+ ALOGW("[vts] provision failed.");
+ return failure();
+ }
+ }
+
+ return openCasSession(token, hidlPvtData);
+}
+
+AssertionResult DescramblerTests::openDescrambler(int32_t demuxId) {
+ ndk::ScopedAStatus status;
+ status = mService->openDescrambler(&mDescrambler);
+ if (!status.isOk()) {
+ ALOGW("[vts] openDescrambler failed.");
+ return failure();
+ }
+
+ status = mDescrambler->setDemuxSource(demuxId);
+ if (!status.isOk()) {
+ ALOGW("[vts] setDemuxSource failed.");
+ return failure();
+ }
+
+ return success();
+}
+
+AssertionResult DescramblerTests::setKeyToken(vector<uint8_t>& token) {
+ ndk::ScopedAStatus status;
+ if (!mDescrambler) {
+ ALOGW("[vts] Descrambler is not opened yet.");
+ return failure();
+ }
+
+ status = mDescrambler->setKeyToken(token);
+ if (!status.isOk()) {
+ ALOGW("[vts] setKeyToken failed.");
+ return failure();
+ }
+
+ return success();
+}
+
+AssertionResult DescramblerTests::addPid(DemuxPid pid,
+ std::shared_ptr<IFilter> optionalSourceFilter) {
+ ndk::ScopedAStatus status;
+ if (!mDescrambler) {
+ ALOGW("[vts] Descrambler is not opened yet.");
+ return failure();
+ }
+
+ status = mDescrambler->addPid(pid, optionalSourceFilter);
+ if (!status.isOk()) {
+ ALOGW("[vts] addPid failed.");
+ return failure();
+ }
+
+ return success();
+}
+
+AssertionResult DescramblerTests::removePid(DemuxPid pid,
+ std::shared_ptr<IFilter> optionalSourceFilter) {
+ ndk::ScopedAStatus status;
+ if (!mDescrambler) {
+ ALOGW("[vts] Descrambler is not opened yet.");
+ return failure();
+ }
+
+ status = mDescrambler->removePid(pid, optionalSourceFilter);
+ if (!status.isOk()) {
+ ALOGW("[vts] removePid failed.");
+ return failure();
+ }
+
+ return success();
+}
+
+AssertionResult DescramblerTests::closeDescrambler() {
+ ndk::ScopedAStatus status;
+ if (!mDescrambler) {
+ ALOGW("[vts] Descrambler is not opened yet.");
+ return failure();
+ }
+
+ status = mDescrambler->close();
+ mDescrambler = nullptr;
+ if (!status.isOk()) {
+ ALOGW("[vts] close Descrambler failed.");
+ return failure();
+ }
+
+ return success();
+}
+
+AssertionResult DescramblerTests::getDemuxPidFromFilterSettings(DemuxFilterType type,
+ const DemuxFilterSettings& settings,
+ DemuxPid& pid) {
+ switch (type.mainType) {
+ case DemuxFilterMainType::TS:
+ if (type.subType.get<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>() ==
+ DemuxTsFilterType::AUDIO ||
+ type.subType.get<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>() ==
+ DemuxTsFilterType::VIDEO) {
+ pid.set<DemuxPid::Tag::tPid>(settings.get<DemuxFilterSettings::Tag::ts>().tpid);
+ } else {
+ ALOGW("[vts] Not a media ts filter!");
+ return failure();
+ }
+ break;
+ case DemuxFilterMainType::MMTP:
+ if (type.subType.get<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>() ==
+ DemuxMmtpFilterType::AUDIO ||
+ type.subType.get<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>() ==
+ DemuxMmtpFilterType::VIDEO) {
+ pid.set<DemuxPid::Tag::mmtpPid>(
+ settings.get<DemuxFilterSettings::Tag::mmtp>().mmtpPid);
+ } else {
+ ALOGW("[vts] Not a media mmtp filter!");
+ return failure();
+ }
+ break;
+ default:
+ ALOGW("[vts] Not a media filter!");
+ return failure();
+ }
+ return success();
+}
diff --git a/tv/tuner/aidl/vts/functional/DescramblerTests.h b/tv/tuner/aidl/vts/functional/DescramblerTests.h
new file mode 100644
index 0000000..f0b7691
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/DescramblerTests.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <fstream>
+#include <iostream>
+#include <map>
+
+#include <android/hardware/cas/1.0/types.h>
+#include <android/hardware/cas/1.2/ICas.h>
+#include <android/hardware/cas/1.2/ICasListener.h>
+#include <android/hardware/cas/1.2/IMediaCasService.h>
+#include <android/hardware/cas/1.2/types.h>
+
+#include <aidl/android/hardware/tv/tuner/IDescrambler.h>
+#include <aidl/android/hardware/tv/tuner/IDvr.h>
+#include <aidl/android/hardware/tv/tuner/IDvrCallback.h>
+#include <aidl/android/hardware/tv/tuner/ITuner.h>
+
+using android::Condition;
+using android::Mutex;
+using android::sp;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::cas::V1_2::ICas;
+using android::hardware::cas::V1_2::ICasListener;
+using android::hardware::cas::V1_2::IMediaCasService;
+using android::hardware::cas::V1_2::ScramblingMode;
+using android::hardware::cas::V1_2::SessionIntent;
+using android::hardware::cas::V1_2::Status;
+using android::hardware::cas::V1_2::StatusEvent;
+
+using ::testing::AssertionResult;
+
+using namespace aidl::android::hardware::tv::tuner;
+
+class MediaCasListener : public ICasListener {
+ public:
+ virtual Return<void> onEvent(int32_t /*event*/, int32_t /*arg*/,
+ const hidl_vec<uint8_t>& /*data*/) override {
+ return Void();
+ }
+
+ virtual Return<void> onSessionEvent(const hidl_vec<uint8_t>& /*sessionId*/, int32_t /*event*/,
+ int32_t /*arg*/,
+ const hidl_vec<uint8_t>& /*data*/) override {
+ return Void();
+ }
+
+ virtual Return<void> onStatusUpdate(StatusEvent /*event*/, int32_t /*arg*/) override {
+ return Void();
+ }
+};
+
+class DescramblerTests {
+ public:
+ void setService(std::shared_ptr<ITuner> tuner) { mService = tuner; }
+ void setCasService(sp<IMediaCasService> casService) { mMediaCasService = casService; }
+
+ AssertionResult setKeyToken(std::vector<uint8_t>& token);
+ AssertionResult openDescrambler(int32_t demuxId);
+ AssertionResult addPid(DemuxPid pid, std::shared_ptr<IFilter> optionalSourceFilter);
+ AssertionResult removePid(DemuxPid pid, std::shared_ptr<IFilter> optionalSourceFilter);
+ AssertionResult closeDescrambler();
+ AssertionResult getKeyToken(int32_t caSystemId, std::string& provisonStr,
+ std::vector<uint8_t>& hidlPvtData, std::vector<uint8_t>& token);
+ AssertionResult getDemuxPidFromFilterSettings(DemuxFilterType type,
+ const DemuxFilterSettings& settings,
+ DemuxPid& pid);
+
+ protected:
+ static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+ static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+ std::shared_ptr<ITuner> mService;
+ std::shared_ptr<IDescrambler> mDescrambler;
+ android::sp<ICas> mCas;
+ android::sp<IMediaCasService> mMediaCasService;
+ android::sp<MediaCasListener> mCasListener;
+
+ private:
+ AssertionResult openCasSession(std::vector<uint8_t>& sessionId,
+ std::vector<uint8_t>& hidlPvtData);
+ AssertionResult createCasPlugin(int32_t caSystemId);
+};
diff --git a/tv/tuner/aidl/vts/functional/DvrTests.cpp b/tv/tuner/aidl/vts/functional/DvrTests.cpp
new file mode 100644
index 0000000..e356099
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/DvrTests.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright 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.
+ */
+
+#include "DvrTests.h"
+
+#include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.h>
+
+void DvrCallback::startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings,
+ MQDesc& playbackMQDescriptor) {
+ mInputDataFile = dataInputFile;
+ mPlaybackSettings = settings;
+ mPlaybackMQ = std::make_unique<FilterMQ>(playbackMQDescriptor, true /* resetPointers */);
+ EXPECT_TRUE(mPlaybackMQ);
+ pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, this);
+ pthread_setname_np(mPlaybackThread, "test_playback_input_loop");
+}
+
+void DvrCallback::stopPlaybackThread() {
+ mPlaybackThreadRunning = false;
+ mKeepWritingPlaybackFMQ = false;
+
+ android::Mutex::Autolock autoLock(mPlaybackThreadLock);
+}
+
+void* DvrCallback::__threadLoopPlayback(void* user) {
+ DvrCallback* const self = static_cast<DvrCallback*>(user);
+ self->playbackThreadLoop();
+ return 0;
+}
+
+void DvrCallback::playbackThreadLoop() {
+ android::Mutex::Autolock autoLock(mPlaybackThreadLock);
+ mPlaybackThreadRunning = true;
+
+ // Create the EventFlag that is used to signal the HAL impl that data have been
+ // written into the Playback FMQ
+ EventFlag* playbackMQEventFlag;
+ EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
+ android::OK);
+
+ int fd = open(mInputDataFile.c_str(), O_RDONLY | O_LARGEFILE);
+ int readBytes;
+ uint32_t regionSize = 0;
+ int8_t* buffer;
+ ALOGW("[vts] playback thread loop start %s", mInputDataFile.c_str());
+ if (fd < 0) {
+ mPlaybackThreadRunning = false;
+ ALOGW("[vts] Error %s", strerror(errno));
+ }
+
+ while (mPlaybackThreadRunning) {
+ while (mKeepWritingPlaybackFMQ) {
+ int totalWrite = mPlaybackMQ->availableToWrite();
+ if (totalWrite * 4 < mPlaybackMQ->getQuantumCount()) {
+ // Wait for the HAL implementation to read more data then write.
+ continue;
+ }
+ AidlMessageQueue<int8_t, SynchronizedReadWrite>::MemTransaction memTx;
+ if (!mPlaybackMQ->beginWrite(totalWrite, &memTx)) {
+ ALOGW("[vts] Fail to write into Playback fmq.");
+ mPlaybackThreadRunning = false;
+ break;
+ }
+ auto first = memTx.getFirstRegion();
+ buffer = first.getAddress();
+ regionSize = first.getLength();
+
+ if (regionSize > 0) {
+ readBytes = read(fd, buffer, regionSize);
+ if (readBytes <= 0) {
+ if (readBytes < 0) {
+ ALOGW("[vts] Read from %s failed.", mInputDataFile.c_str());
+ } else {
+ ALOGW("[vts] playback input EOF.");
+ }
+ mPlaybackThreadRunning = false;
+ break;
+ }
+ }
+ if (regionSize == 0 || (readBytes == regionSize && regionSize < totalWrite)) {
+ auto second = memTx.getSecondRegion();
+ buffer = second.getAddress();
+ regionSize = second.getLength();
+ int ret = read(fd, buffer, regionSize);
+ if (ret <= 0) {
+ if (ret < 0) {
+ ALOGW("[vts] Read from %s failed.", mInputDataFile.c_str());
+ } else {
+ ALOGW("[vts] playback input EOF.");
+ }
+ mPlaybackThreadRunning = false;
+ break;
+ }
+ readBytes += ret;
+ }
+ if (!mPlaybackMQ->commitWrite(readBytes)) {
+ ALOGW("[vts] Failed to commit write playback fmq.");
+ mPlaybackThreadRunning = false;
+ break;
+ }
+ playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+ }
+ }
+
+ mPlaybackThreadRunning = false;
+ ALOGW("[vts] Playback thread end.");
+ close(fd);
+}
+
+void DvrCallback::testRecordOutput() {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (mDataOutputBuffer.empty()) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "record output matching pid does not output within timeout";
+ stopRecordThread();
+ return;
+ }
+ }
+ stopRecordThread();
+ ALOGW("[vts] record pass and stop");
+}
+
+void DvrCallback::startRecordOutputThread(RecordSettings recordSettings,
+ MQDesc& recordMQDescriptor) {
+ mRecordMQ = std::make_unique<FilterMQ>(recordMQDescriptor, true /* resetPointers */);
+ EXPECT_TRUE(mRecordMQ);
+ struct RecordThreadArgs* threadArgs =
+ (struct RecordThreadArgs*)malloc(sizeof(struct RecordThreadArgs));
+ threadArgs->user = this;
+ threadArgs->recordSettings = &recordSettings;
+ threadArgs->keepReadingRecordFMQ = &mKeepReadingRecordFMQ;
+
+ pthread_create(&mRecordThread, NULL, __threadLoopRecord, (void*)threadArgs);
+ pthread_setname_np(mRecordThread, "test_record_input_loop");
+}
+
+void* DvrCallback::__threadLoopRecord(void* threadArgs) {
+ DvrCallback* const self =
+ static_cast<DvrCallback*>(((struct RecordThreadArgs*)threadArgs)->user);
+ self->recordThreadLoop(((struct RecordThreadArgs*)threadArgs)->recordSettings,
+ ((struct RecordThreadArgs*)threadArgs)->keepReadingRecordFMQ);
+ return 0;
+}
+
+void DvrCallback::recordThreadLoop(RecordSettings* /*recordSettings*/, bool* keepReadingRecordFMQ) {
+ ALOGD("[vts] DvrCallback record threadLoop start.");
+ android::Mutex::Autolock autoLock(mRecordThreadLock);
+ mRecordThreadRunning = true;
+ mKeepReadingRecordFMQ = true;
+
+ // Create the EventFlag that is used to signal the HAL impl that data have been
+ // read from the Record FMQ
+ EventFlag* recordMQEventFlag;
+ EXPECT_TRUE(EventFlag::createEventFlag(mRecordMQ->getEventFlagWord(), &recordMQEventFlag) ==
+ android::OK);
+
+ while (mRecordThreadRunning) {
+ while (*keepReadingRecordFMQ) {
+ uint32_t efState = 0;
+ android::status_t status = recordMQEventFlag->wait(
+ static_cast<int32_t>(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT,
+ true /* retry on spurious wake */);
+ if (status != android::OK) {
+ ALOGD("[vts] wait for data ready on the record FMQ");
+ continue;
+ }
+ // Our current implementation filter the data and write it into the filter FMQ
+ // immediately after the DATA_READY from the VTS/framework
+ if (!readRecordFMQ()) {
+ ALOGD("[vts] record data failed to be filtered. Ending thread");
+ mRecordThreadRunning = false;
+ break;
+ }
+ }
+ }
+
+ mRecordThreadRunning = false;
+ ALOGD("[vts] record thread ended.");
+}
+
+bool DvrCallback::readRecordFMQ() {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ bool result = false;
+ int readSize = mRecordMQ->availableToRead();
+ mDataOutputBuffer.clear();
+ mDataOutputBuffer.resize(readSize);
+ result = mRecordMQ->read(mDataOutputBuffer.data(), readSize);
+ EXPECT_TRUE(result) << "can't read from Record MQ";
+ mMsgCondition.signal();
+ return result;
+}
+
+void DvrCallback::stopRecordThread() {
+ mKeepReadingRecordFMQ = false;
+ mRecordThreadRunning = false;
+}
+
+AssertionResult DvrTests::openDvrInDemux(DvrType type, int32_t bufferSize) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+
+ // Create dvr callback
+ if (type == DvrType::PLAYBACK) {
+ mDvrPlaybackCallback = ndk::SharedRefBase::make<DvrCallback>();
+ status = mDemux->openDvr(type, bufferSize, mDvrPlaybackCallback, &mDvrPlayback);
+ if (status.isOk()) {
+ mDvrPlaybackCallback->setDvr(mDvrPlayback);
+ }
+ }
+
+ if (type == DvrType::RECORD) {
+ mDvrRecordCallback = ndk::SharedRefBase::make<DvrCallback>();
+ status = mDemux->openDvr(type, bufferSize, mDvrRecordCallback, &mDvrRecord);
+ if (status.isOk()) {
+ mDvrRecordCallback->setDvr(mDvrRecord);
+ }
+ }
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::configDvrPlayback(DvrSettings setting) {
+ ndk::ScopedAStatus status = mDvrPlayback->configure(setting);
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::configDvrRecord(DvrSettings setting) {
+ ndk::ScopedAStatus status = mDvrRecord->configure(setting);
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::getDvrPlaybackMQDescriptor() {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
+
+ status = mDvrPlayback->getQueueDesc(&mDvrPlaybackMQDescriptor);
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::getDvrRecordMQDescriptor() {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+ status = mDvrRecord->getQueueDesc(&mDvrRecordMQDescriptor);
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::attachFilterToDvr(std::shared_ptr<IFilter> filter) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+ status = mDvrRecord->attachFilter(filter);
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::detachFilterToDvr(std::shared_ptr<IFilter> filter) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+ status = mDvrRecord->detachFilter(filter);
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::startDvrPlayback() {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
+
+ status = mDvrPlayback->start();
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::stopDvrPlayback() {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
+
+ status = mDvrPlayback->stop();
+
+ return AssertionResult(status.isOk());
+}
+
+void DvrTests::closeDvrPlayback() {
+ ASSERT_TRUE(mDemux);
+ ASSERT_TRUE(mDvrPlayback);
+ ASSERT_TRUE(mDvrPlayback->close().isOk());
+}
+
+AssertionResult DvrTests::startDvrRecord() {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+ status = mDvrRecord->start();
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::stopDvrRecord() {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+ status = mDvrRecord->stop();
+
+ return AssertionResult(status.isOk());
+}
+
+void DvrTests::closeDvrRecord() {
+ ASSERT_TRUE(mDemux);
+ ASSERT_TRUE(mDvrRecord);
+ ASSERT_TRUE(mDvrRecord->close().isOk());
+}
diff --git a/tv/tuner/aidl/vts/functional/DvrTests.h b/tv/tuner/aidl/vts/functional/DvrTests.h
new file mode 100644
index 0000000..bda57b3
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/DvrTests.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <fcntl.h>
+#include <fmq/AidlMessageQueue.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <fstream>
+#include <iostream>
+#include <map>
+
+#include <aidl/android/hardware/tv/tuner/BnDvrCallback.h>
+#include <aidl/android/hardware/tv/tuner/IDvr.h>
+#include <aidl/android/hardware/tv/tuner/ITuner.h>
+
+#include "FilterTests.h"
+
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::android::AidlMessageQueue;
+using ::android::Condition;
+using ::android::Mutex;
+using ::android::hardware::EventFlag;
+
+using namespace aidl::android::hardware::tv::tuner;
+using namespace std;
+
+#define WAIT_TIMEOUT 3000000000
+
+class DvrCallback : public BnDvrCallback {
+ public:
+ virtual ::ndk::ScopedAStatus onRecordStatus(RecordStatus status) override {
+ ALOGD("[vts] record status %hhu", status);
+ switch (status) {
+ case RecordStatus::DATA_READY:
+ break;
+ case RecordStatus::LOW_WATER:
+ break;
+ case RecordStatus::HIGH_WATER:
+ case RecordStatus::OVERFLOW:
+ ALOGD("[vts] record overflow. Flushing.");
+ EXPECT_TRUE(mDvr) << "Dvr callback is not set with an IDvr";
+ if (mDvr) {
+ ndk::ScopedAStatus result = mDvr->flush();
+ ALOGD("[vts] Flushing result %s.", result.getMessage());
+ }
+ break;
+ }
+ return ndk::ScopedAStatus::ok();
+ }
+
+ virtual ::ndk::ScopedAStatus onPlaybackStatus(PlaybackStatus status) override {
+ // android::Mutex::Autolock autoLock(mMsgLock);
+ ALOGD("[vts] playback status %d", status);
+ switch (status) {
+ case PlaybackStatus::SPACE_EMPTY:
+ case PlaybackStatus::SPACE_ALMOST_EMPTY:
+ ALOGD("[vts] keep playback inputing %d", status);
+ mKeepWritingPlaybackFMQ = true;
+ break;
+ case PlaybackStatus::SPACE_ALMOST_FULL:
+ case PlaybackStatus::SPACE_FULL:
+ ALOGD("[vts] stop playback inputing %d", status);
+ mKeepWritingPlaybackFMQ = false;
+ break;
+ }
+ return ndk::ScopedAStatus::ok();
+ }
+
+ void stopPlaybackThread();
+ void testRecordOutput();
+ void stopRecordThread();
+
+ void startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings,
+ MQDesc& playbackMQDescriptor);
+ void startRecordOutputThread(RecordSettings recordSettings, MQDesc& recordMQDescriptor);
+ static void* __threadLoopPlayback(void* user);
+ static void* __threadLoopRecord(void* threadArgs);
+ void playbackThreadLoop();
+ void recordThreadLoop(RecordSettings* recordSetting, bool* keepWritingPlaybackFMQ);
+
+ bool readRecordFMQ();
+
+ void setDvr(std::shared_ptr<IDvr> dvr) { mDvr = dvr; }
+
+ private:
+ struct RecordThreadArgs {
+ DvrCallback* user;
+ RecordSettings* recordSettings;
+ bool* keepReadingRecordFMQ;
+ };
+ // uint16_t mDataLength = 0;
+ std::vector<int8_t> mDataOutputBuffer;
+
+ std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterMQ;
+ std::unique_ptr<FilterMQ> mPlaybackMQ;
+ std::unique_ptr<FilterMQ> mRecordMQ;
+ std::map<uint32_t, EventFlag*> mFilterMQEventFlag;
+
+ android::Mutex mMsgLock;
+ android::Mutex mPlaybackThreadLock;
+ android::Mutex mRecordThreadLock;
+ android::Condition mMsgCondition;
+
+ bool mKeepWritingPlaybackFMQ = true;
+ bool mKeepReadingRecordFMQ = true;
+ bool mPlaybackThreadRunning;
+ bool mRecordThreadRunning;
+ pthread_t mPlaybackThread;
+ pthread_t mRecordThread;
+ string mInputDataFile;
+ PlaybackSettings mPlaybackSettings;
+
+ std::shared_ptr<IDvr> mDvr = nullptr;
+
+ // int mPidFilterOutputCount = 0;
+};
+
+class DvrTests {
+ public:
+ void setService(std::shared_ptr<ITuner> tuner) { mService = tuner; }
+ void setDemux(std::shared_ptr<IDemux> demux) { mDemux = demux; }
+
+ void startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings) {
+ mDvrPlaybackCallback->startPlaybackInputThread(dataInputFile, settings,
+ mDvrPlaybackMQDescriptor);
+ };
+
+ void startRecordOutputThread(RecordSettings settings) {
+ mDvrRecordCallback->startRecordOutputThread(settings, mDvrRecordMQDescriptor);
+ };
+
+ void stopPlaybackThread() { mDvrPlaybackCallback->stopPlaybackThread(); }
+ void testRecordOutput() { mDvrRecordCallback->testRecordOutput(); }
+ void stopRecordThread() { mDvrRecordCallback->stopRecordThread(); }
+
+ AssertionResult openDvrInDemux(DvrType type, int32_t bufferSize);
+ AssertionResult configDvrPlayback(DvrSettings setting);
+ AssertionResult configDvrRecord(DvrSettings setting);
+ AssertionResult getDvrPlaybackMQDescriptor();
+ AssertionResult getDvrRecordMQDescriptor();
+ AssertionResult attachFilterToDvr(std::shared_ptr<IFilter> filter);
+ AssertionResult detachFilterToDvr(std::shared_ptr<IFilter> filter);
+ AssertionResult stopDvrPlayback();
+ AssertionResult startDvrPlayback();
+ AssertionResult stopDvrRecord();
+ AssertionResult startDvrRecord();
+ void closeDvrPlayback();
+ void closeDvrRecord();
+
+ protected:
+ static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+ static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+ std::shared_ptr<ITuner> mService;
+ std::shared_ptr<IDvr> mDvrPlayback;
+ std::shared_ptr<IDvr> mDvrRecord;
+ std::shared_ptr<IDemux> mDemux;
+ std::shared_ptr<DvrCallback> mDvrPlaybackCallback;
+ std::shared_ptr<DvrCallback> mDvrRecordCallback;
+ MQDesc mDvrPlaybackMQDescriptor;
+ MQDesc mDvrRecordMQDescriptor;
+};
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.cpp b/tv/tuner/aidl/vts/functional/FilterTests.cpp
new file mode 100644
index 0000000..381475a
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/FilterTests.cpp
@@ -0,0 +1,362 @@
+/*
+ * Copyright 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.
+ */
+
+#include "FilterTests.h"
+
+#include <inttypes.h>
+
+#include <aidl/android/hardware/tv/tuner/DemuxFilterMonitorEventType.h>
+#include <aidlcommonsupport/NativeHandle.h>
+
+using ::aidl::android::hardware::common::NativeHandle;
+
+void FilterCallback::testFilterDataOutput() {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (mPidFilterOutputCount < 1) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "filter output matching pid does not output within timeout";
+ return;
+ }
+ }
+ mPidFilterOutputCount = 0;
+ ALOGW("[vts] pass and stop");
+}
+
+void FilterCallback::testFilterScramblingEvent() {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (mScramblingStatusEvent < 1) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "scrambling event does not output within timeout";
+ return;
+ }
+ }
+ mScramblingStatusEvent = 0;
+ ALOGW("[vts] pass and stop");
+}
+
+void FilterCallback::testFilterIpCidEvent() {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (mIpCidEvent < 1) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "ip cid change event does not output within timeout";
+ return;
+ }
+ }
+ mIpCidEvent = 0;
+ ALOGW("[vts] pass and stop");
+}
+
+void FilterCallback::testStartIdAfterReconfigure() {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (!mStartIdReceived) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "does not receive start id within timeout";
+ return;
+ }
+ }
+ mStartIdReceived = false;
+ ALOGW("[vts] pass and stop");
+}
+
+void FilterCallback::readFilterEventsData(const vector<DemuxFilterEvent>& events) {
+ ALOGW("[vts] reading filter event");
+ // todo separate filter handlers
+ for (int i = 0; i < events.size(); i++) {
+ switch (events[i].getTag()) {
+ case DemuxFilterEvent::Tag::media:
+ ALOGD("[vts] Media filter event, avMemHandle numFds=%zu.",
+ events[i].get<DemuxFilterEvent::Tag::media>().avMemory.fds.size());
+ dumpAvData(events[i].get<DemuxFilterEvent::Tag::media>());
+ break;
+ case DemuxFilterEvent::Tag::tsRecord:
+ ALOGD("[vts] TS record filter event, pts=%" PRIu64 ", firstMbInSlice=%d",
+ events[i].get<DemuxFilterEvent::Tag::tsRecord>().pts,
+ events[i].get<DemuxFilterEvent::Tag::tsRecord>().firstMbInSlice);
+ break;
+ case DemuxFilterEvent::Tag::mmtpRecord:
+ ALOGD("[vts] MMTP record filter event, pts=%" PRIu64
+ ", firstMbInSlice=%d, mpuSequenceNumber=%d, tsIndexMask=%d",
+ events[i].get<DemuxFilterEvent::Tag::mmtpRecord>().pts,
+ events[i].get<DemuxFilterEvent::Tag::mmtpRecord>().firstMbInSlice,
+ events[i].get<DemuxFilterEvent::Tag::mmtpRecord>().mpuSequenceNumber,
+ events[i].get<DemuxFilterEvent::Tag::mmtpRecord>().tsIndexMask);
+ break;
+ case DemuxFilterEvent::Tag::monitorEvent:
+ switch (events[i].get<DemuxFilterEvent::Tag::monitorEvent>().getTag()) {
+ case DemuxFilterMonitorEvent::Tag::scramblingStatus:
+ mScramblingStatusEvent++;
+ break;
+ case DemuxFilterMonitorEvent::Tag::cid:
+ mIpCidEvent++;
+ break;
+ default:
+ break;
+ }
+ break;
+ case DemuxFilterEvent::Tag::startId:
+ ALOGD("[vts] Restart filter event, startId=%d",
+ events[i].get<DemuxFilterEvent::Tag::startId>());
+ mStartIdReceived = true;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bool FilterCallback::dumpAvData(const DemuxFilterMediaEvent& event) {
+ int32_t length = event.dataLength;
+ int32_t offset = event.offset;
+ int av_fd;
+ // read data from buffer pointed by a handle
+ if (event.avMemory.fds.size() == 0) {
+ if (mAvSharedHandle == nullptr) {
+ return false;
+ }
+ av_fd = mAvSharedHandle->data[0];
+ } else {
+ av_fd = event.avMemory.fds[0].get();
+ }
+ uint8_t* buffer = static_cast<uint8_t*>(
+ mmap(NULL, length + offset, PROT_READ | PROT_WRITE, MAP_SHARED, av_fd, 0));
+ if (buffer == MAP_FAILED) {
+ ALOGE("[vts] fail to allocate av buffer, errno=%d", errno);
+ return false;
+ }
+ uint8_t output[length + 1];
+ memcpy(output, buffer + offset, length);
+ // print buffer and check with golden output.
+ return true;
+}
+
+AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type, int32_t bufferSize) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+
+ // Create demux callback
+ mFilterCallback = ndk::SharedRefBase::make<FilterCallback>();
+
+ // Add filter to the local demux
+ status = mDemux->openFilter(type, bufferSize, mFilterCallback, &mFilter);
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::getNewlyOpenedFilterId_64bit(int64_t& filterId) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mFilter) << "Test with openFilterInDemux first.";
+ EXPECT_TRUE(mFilterCallback) << "Test with openFilterInDemux first.";
+
+ status = mFilter->getId64Bit(&mFilterId);
+ if (status.isOk()) {
+ mFilterCallback->setFilterId(mFilterId);
+ mFilterCallback->setFilterInterface(mFilter);
+ mUsedFilterIds.insert(mUsedFilterIds.end(), mFilterId);
+ mFilters[mFilterId] = mFilter;
+ mFilterCallbacks[mFilterId] = mFilterCallback;
+ filterId = mFilterId;
+
+ // Check getId() too.
+ int32_t filterId32Bit;
+ status = mFilter->getId(&filterId32Bit);
+ }
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::getSharedAvMemoryHandle(int64_t filterId) {
+ EXPECT_TRUE(mFilters[filterId]) << "Open media filter first.";
+ NativeHandle avMemory;
+ int64_t avMemSize;
+ ndk::ScopedAStatus status = mFilters[filterId]->getAvSharedHandle(&avMemory, &avMemSize);
+ if (status.isOk()) {
+ mAvSharedHandle = android::dupFromAidl(avMemory);
+ mFilterCallbacks[mFilterId]->setSharedHandle(mAvSharedHandle);
+ mFilterCallbacks[mFilterId]->setMemSize(avMemSize);
+ }
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::releaseShareAvHandle(int64_t filterId) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mFilters[filterId]) << "Open media filter first.";
+ EXPECT_TRUE(mAvSharedHandle) << "No shared av handle to release.";
+ status = mFilters[filterId]->releaseAvHandle(::android::makeToAidl(mAvSharedHandle),
+ 0 /*dataId*/);
+ native_handle_close(mAvSharedHandle);
+ native_handle_delete(mAvSharedHandle);
+ mAvSharedHandle = nullptr;
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::configFilter(DemuxFilterSettings setting, int64_t filterId) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+ status = mFilters[filterId]->configure(setting);
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::configAvFilterStreamType(AvStreamType type, int64_t filterId) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+
+ status = mFilters[filterId]->configureAvStreamType(type);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::configIpFilterCid(int32_t ipCid, int64_t filterId) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mFilters[filterId]) << "Open Ip filter first.";
+
+ status = mFilters[filterId]->configureIpCid(ipCid);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::getFilterMQDescriptor(int64_t filterId, bool getMqDesc) {
+ if (!getMqDesc) {
+ ALOGE("[vts] Filter does not need FMQ.");
+ return success();
+ }
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+ EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
+
+ status = mFilters[filterId]->getQueueDesc(&mFilterMQDescriptor);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::startFilter(int64_t filterId) {
+ EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+
+ ndk::ScopedAStatus status = mFilters[filterId]->start();
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::stopFilter(int64_t filterId) {
+ EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+
+ ndk::ScopedAStatus status = mFilters[filterId]->stop();
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::closeFilter(int64_t filterId) {
+ EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+ ndk::ScopedAStatus status = mFilters[filterId]->close();
+ if (status.isOk()) {
+ for (int i = 0; i < mUsedFilterIds.size(); i++) {
+ if (mUsedFilterIds[i] == filterId) {
+ mUsedFilterIds.erase(mUsedFilterIds.begin() + i);
+ break;
+ }
+ }
+ mFilterCallbacks.erase(filterId);
+ mFilters.erase(filterId);
+ }
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::configureMonitorEvent(int64_t filterId, int32_t monitorEventTypes) {
+ EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+ ndk::ScopedAStatus status;
+
+ status = mFilters[filterId]->configureMonitorEvent(monitorEventTypes);
+ if (monitorEventTypes & static_cast<int32_t>(DemuxFilterMonitorEventType::SCRAMBLING_STATUS)) {
+ mFilterCallbacks[filterId]->testFilterScramblingEvent();
+ }
+ if (monitorEventTypes & static_cast<int32_t>(DemuxFilterMonitorEventType::IP_CID_CHANGE)) {
+ mFilterCallbacks[filterId]->testFilterIpCidEvent();
+ }
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::startIdTest(int64_t filterId) {
+ EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
+ mFilterCallbacks[filterId]->testStartIdAfterReconfigure();
+ return AssertionResult(true);
+}
+
+AssertionResult FilterTests::openTimeFilterInDemux() {
+ if (!mDemux) {
+ ALOGW("[vts] Test with openDemux first.");
+ return failure();
+ }
+
+ // Add time filter to the local demux
+ auto status = mDemux->openTimeFilter(&mTimeFilter);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::setTimeStamp(int64_t timeStamp) {
+ if (!mTimeFilter) {
+ ALOGW("[vts] Test with openTimeFilterInDemux first.");
+ return failure();
+ }
+
+ mBeginTimeStamp = timeStamp;
+ return AssertionResult(mTimeFilter->setTimeStamp(timeStamp).isOk());
+}
+
+AssertionResult FilterTests::getTimeStamp() {
+ if (!mTimeFilter) {
+ ALOGW("[vts] Test with openTimeFilterInDemux first.");
+ return failure();
+ }
+
+ int64_t timeStamp;
+ auto status = mTimeFilter->getTimeStamp(&timeStamp);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::setFilterDataSource(int64_t sourceFilterId, int64_t sinkFilterId) {
+ if (!mFilters[sourceFilterId] || !mFilters[sinkFilterId]) {
+ ALOGE("[vts] setFilterDataSource filter not opened.");
+ return failure();
+ }
+
+ auto status = mFilters[sinkFilterId]->setDataSource(mFilters[sourceFilterId]);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::setFilterDataSourceToDemux(int64_t filterId) {
+ if (!mFilters[filterId]) {
+ ALOGE("[vts] setFilterDataSourceToDemux filter not opened.");
+ return failure();
+ }
+
+ auto status = mFilters[filterId]->setDataSource(nullptr);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::clearTimeStamp() {
+ if (!mTimeFilter) {
+ ALOGW("[vts] Test with openTimeFilterInDemux first.");
+ return failure();
+ }
+
+ return AssertionResult(mTimeFilter->clearTimeStamp().isOk());
+}
+
+AssertionResult FilterTests::closeTimeFilter() {
+ if (!mTimeFilter) {
+ ALOGW("[vts] Test with openTimeFilterInDemux first.");
+ return failure();
+ }
+
+ return AssertionResult(mTimeFilter->close().isOk());
+}
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.h b/tv/tuner/aidl/vts/functional/FilterTests.h
new file mode 100644
index 0000000..91a0a4a
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/FilterTests.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/tv/tuner/BnFilterCallback.h>
+#include <aidl/android/hardware/tv/tuner/IDemux.h>
+#include <aidl/android/hardware/tv/tuner/IFilter.h>
+#include <aidl/android/hardware/tv/tuner/ITuner.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <map>
+
+#include <fmq/AidlMessageQueue.h>
+
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::android::AidlMessageQueue;
+using ::android::Condition;
+using ::android::Mutex;
+using ::android::hardware::EventFlag;
+
+using ::testing::AssertionResult;
+
+using namespace aidl::android::hardware::tv::tuner;
+using namespace std;
+
+enum FilterEventType : uint8_t {
+ UNDEFINED,
+ SECTION,
+ MEDIA,
+ PES,
+ RECORD,
+ MMTPRECORD,
+ DOWNLOAD,
+ TEMI,
+};
+
+using FilterMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using MQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
+#define WAIT_TIMEOUT 3000000000
+
+class FilterCallback : public BnFilterCallback {
+ public:
+ virtual ::ndk::ScopedAStatus onFilterEvent(const vector<DemuxFilterEvent>& events) override {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ // Temprarily we treat the first coming back filter data on the matching pid a success
+ // once all of the MQ are cleared, means we got all the expected output
+ readFilterEventsData(events);
+ mPidFilterOutputCount++;
+ mMsgCondition.signal();
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ virtual ::ndk::ScopedAStatus onFilterStatus(const DemuxFilterStatus /*status*/) override {
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ void setFilterId(int32_t filterId) { mFilterId = filterId; }
+ void setFilterInterface(std::shared_ptr<IFilter> filter) { mFilter = filter; }
+ void setFilterEventType(FilterEventType type) { mFilterEventType = type; }
+ void setSharedHandle(native_handle_t* sharedHandle) { mAvSharedHandle = sharedHandle; }
+ void setMemSize(uint64_t size) { mAvSharedMemSize = size; }
+
+ void testFilterDataOutput();
+ void testFilterScramblingEvent();
+ void testFilterIpCidEvent();
+ void testStartIdAfterReconfigure();
+
+ void readFilterEventsData(const vector<DemuxFilterEvent>& events);
+ bool dumpAvData(const DemuxFilterMediaEvent& event);
+
+ private:
+ int32_t mFilterId;
+ std::shared_ptr<IFilter> mFilter;
+ FilterEventType mFilterEventType;
+
+ native_handle_t* mAvSharedHandle = nullptr;
+ uint64_t mAvSharedMemSize = -1;
+
+ android::Mutex mMsgLock;
+ android::Mutex mFilterOutputLock;
+ android::Condition mMsgCondition;
+
+ int mPidFilterOutputCount = 0;
+ int mScramblingStatusEvent = 0;
+ int mIpCidEvent = 0;
+ bool mStartIdReceived = false;
+};
+
+class FilterTests {
+ public:
+ void setService(std::shared_ptr<ITuner> tuner) { mService = tuner; }
+ void setDemux(std::shared_ptr<IDemux> demux) { mDemux = demux; }
+ std::shared_ptr<IFilter> getFilterById(int64_t filterId) { return mFilters[filterId]; }
+
+ map<int64_t, std::shared_ptr<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
+
+ AssertionResult openFilterInDemux(DemuxFilterType type, int32_t bufferSize);
+ AssertionResult getNewlyOpenedFilterId_64bit(int64_t& filterId);
+ AssertionResult getSharedAvMemoryHandle(int64_t filterId);
+ AssertionResult releaseShareAvHandle(int64_t filterId);
+ AssertionResult configFilter(DemuxFilterSettings setting, int64_t filterId);
+ AssertionResult configAvFilterStreamType(AvStreamType type, int64_t filterId);
+ AssertionResult configIpFilterCid(int32_t ipCid, int64_t filterId);
+ AssertionResult configureMonitorEvent(int64_t filterId, int32_t monitorEventTypes);
+ AssertionResult getFilterMQDescriptor(int64_t filterId, bool getMqDesc);
+ AssertionResult startFilter(int64_t filterId);
+ AssertionResult stopFilter(int64_t filterId);
+ AssertionResult closeFilter(int64_t filterId);
+ AssertionResult startIdTest(int64_t filterId);
+
+ AssertionResult openTimeFilterInDemux();
+ AssertionResult setTimeStamp(int64_t timeStamp);
+ AssertionResult getTimeStamp();
+ AssertionResult setFilterDataSource(int64_t sourceFilterId, int64_t sinkFilterId);
+ AssertionResult setFilterDataSourceToDemux(int64_t filterId);
+ AssertionResult clearTimeStamp();
+ AssertionResult closeTimeFilter();
+
+ protected:
+ static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+ static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+ std::shared_ptr<ITuner> mService;
+ std::shared_ptr<IFilter> mFilter;
+ std::shared_ptr<IDemux> mDemux;
+ std::shared_ptr<ITimeFilter> mTimeFilter;
+ map<int64_t, std::shared_ptr<IFilter>> mFilters;
+ map<int64_t, std::shared_ptr<FilterCallback>> mFilterCallbacks;
+
+ std::shared_ptr<FilterCallback> mFilterCallback;
+ MQDesc mFilterMQDescriptor;
+ vector<int64_t> mUsedFilterIds;
+
+ native_handle_t* mAvSharedHandle = nullptr;
+ int64_t mFilterId = -1;
+ int64_t mBeginTimeStamp;
+};
diff --git a/tv/tuner/aidl/vts/functional/FrontendTests.cpp b/tv/tuner/aidl/vts/functional/FrontendTests.cpp
new file mode 100644
index 0000000..93b7976
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/FrontendTests.cpp
@@ -0,0 +1,486 @@
+/*
+ * Copyright 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.
+ */
+
+#include "FrontendTests.h"
+
+ndk::ScopedAStatus FrontendCallback::onEvent(FrontendEventType frontendEventType) {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ ALOGD("[vts] frontend event received. Type: %d", frontendEventType);
+ mEventReceived = true;
+ mMsgCondition.signal();
+ switch (frontendEventType) {
+ case FrontendEventType::LOCKED:
+ mLockMsgReceived = true;
+ mLockMsgCondition.signal();
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FrontendCallback::onScanMessage(FrontendScanMessageType type,
+ const FrontendScanMessage& message) {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (!mScanMsgProcessed) {
+ mMsgCondition.wait(mMsgLock);
+ }
+ ALOGD("[vts] frontend scan message. Type: %d", type);
+ switch (message.getTag()) {
+ case FrontendScanMessage::modulation:
+ readFrontendScanMessage_Modulation(message.get<FrontendScanMessage::Tag::modulation>());
+ break;
+ case FrontendScanMessage::Tag::isHighPriority:
+ ALOGD("[vts] frontend scan message high priority: %d",
+ message.get<FrontendScanMessage::Tag::isHighPriority>());
+ break;
+ case FrontendScanMessage::Tag::annex:
+ ALOGD("[vts] frontend scan message dvbc annex: %hhu",
+ message.get<FrontendScanMessage::Tag::annex>());
+ break;
+ default:
+ break;
+ }
+ mScanMessageReceived = true;
+ mScanMsgProcessed = false;
+ mScanMessageType = type;
+ mScanMessage = message;
+ mMsgCondition.signal();
+ return ndk::ScopedAStatus::ok();
+}
+
+void FrontendCallback::readFrontendScanMessage_Modulation(FrontendModulation modulation) {
+ switch (modulation.getTag()) {
+ case FrontendModulation::Tag::dvbc:
+ ALOGD("[vts] frontend scan message modulation dvbc: %d",
+ modulation.get<FrontendModulation::Tag::dvbc>());
+ break;
+ case FrontendModulation::Tag::dvbs:
+ ALOGD("[vts] frontend scan message modulation dvbs: %d",
+ modulation.get<FrontendModulation::Tag::dvbs>());
+ break;
+ case FrontendModulation::Tag::isdbs:
+ ALOGD("[vts] frontend scan message modulation isdbs: %d",
+ modulation.get<FrontendModulation::Tag::isdbs>());
+ break;
+ case FrontendModulation::Tag::isdbs3:
+ ALOGD("[vts] frontend scan message modulation isdbs3: %d",
+ modulation.get<FrontendModulation::Tag::isdbs3>());
+ break;
+ case FrontendModulation::Tag::isdbt:
+ ALOGD("[vts] frontend scan message modulation isdbt: %d",
+ modulation.get<FrontendModulation::Tag::isdbt>());
+ break;
+ case FrontendModulation::Tag::atsc:
+ ALOGD("[vts] frontend scan message modulation atsc: %d",
+ modulation.get<FrontendModulation::Tag::atsc>());
+ break;
+ case FrontendModulation::Tag::atsc3:
+ ALOGD("[vts] frontend scan message modulation atsc3: %d",
+ modulation.get<FrontendModulation::Tag::atsc3>());
+ break;
+ case FrontendModulation::Tag::dvbt:
+ ALOGD("[vts] frontend scan message modulation dvbt: %d",
+ modulation.get<FrontendModulation::Tag::dvbt>());
+ break;
+ default:
+ break;
+ }
+}
+
+void FrontendCallback::tuneTestOnLock(std::shared_ptr<IFrontend>& frontend,
+ FrontendSettings settings) {
+ ndk::ScopedAStatus result = frontend->tune(settings);
+ EXPECT_TRUE(result.isOk());
+
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (!mLockMsgReceived) {
+ if (-ETIMEDOUT == mLockMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "Event LOCKED not received within timeout";
+ mLockMsgReceived = false;
+ return;
+ }
+ }
+ mLockMsgReceived = false;
+}
+
+void FrontendCallback::scanTest(std::shared_ptr<IFrontend>& frontend, FrontendConfig config,
+ FrontendScanType type) {
+ int32_t targetFrequency = getTargetFrequency(config.settings);
+ if (type == FrontendScanType::SCAN_BLIND) {
+ // reset the frequency in the scan configuration to test blind scan. The settings param of
+ // passed in means the real input config on the transponder connected to the DUT.
+ // We want the blind the test to start from lower frequency than this to check the blind
+ // scan implementation.
+ resetBlindScanStartingFrequency(config, targetFrequency - 100);
+ }
+
+ ndk::ScopedAStatus result = frontend->scan(config.settings, type);
+ EXPECT_TRUE(result.isOk());
+
+ bool scanMsgLockedReceived = false;
+ bool targetFrequencyReceived = false;
+
+ android::Mutex::Autolock autoLock(mMsgLock);
+wait:
+ while (!mScanMessageReceived) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "Scan message not received within timeout";
+ mScanMessageReceived = false;
+ mScanMsgProcessed = true;
+ return;
+ }
+ }
+
+ if (mScanMessageType != FrontendScanMessageType::END) {
+ if (mScanMessageType == FrontendScanMessageType::LOCKED) {
+ scanMsgLockedReceived = true;
+ result = frontend->scan(config.settings, type);
+ EXPECT_TRUE(result.isOk());
+ }
+
+ if (mScanMessageType == FrontendScanMessageType::FREQUENCY) {
+ targetFrequencyReceived =
+ mScanMessage.get<FrontendScanMessage::Tag::frequencies>().size() > 0 &&
+ mScanMessage.get<FrontendScanMessage::Tag::frequencies>()[0] == targetFrequency;
+ }
+
+ if (mScanMessageType == FrontendScanMessageType::PROGRESS_PERCENT) {
+ ALOGD("[vts] Scan in progress...[%d%%]",
+ mScanMessage.get<FrontendScanMessage::Tag::progressPercent>());
+ }
+
+ mScanMessageReceived = false;
+ mScanMsgProcessed = true;
+ mMsgCondition.signal();
+ goto wait;
+ }
+
+ EXPECT_TRUE(scanMsgLockedReceived) << "Scan message LOCKED not received before END";
+ EXPECT_TRUE(targetFrequencyReceived) << "frequency not received before LOCKED on blindScan";
+ mScanMessageReceived = false;
+ mScanMsgProcessed = true;
+}
+
+int32_t FrontendCallback::getTargetFrequency(FrontendSettings& settings) {
+ switch (settings.getTag()) {
+ case FrontendSettings::Tag::analog:
+ return settings.get<FrontendSettings::Tag::analog>().frequency;
+ case FrontendSettings::Tag::atsc:
+ return settings.get<FrontendSettings::Tag::atsc>().frequency;
+ case FrontendSettings::Tag::atsc3:
+ return settings.get<FrontendSettings::Tag::atsc3>().frequency;
+ case FrontendSettings::Tag::dvbc:
+ return settings.get<FrontendSettings::Tag::dvbc>().frequency;
+ case FrontendSettings::Tag::dvbs:
+ return settings.get<FrontendSettings::Tag::dvbs>().frequency;
+ case FrontendSettings::Tag::dvbt:
+ return settings.get<FrontendSettings::Tag::dvbt>().frequency;
+ case FrontendSettings::Tag::isdbs:
+ return settings.get<FrontendSettings::Tag::isdbs>().frequency;
+ case FrontendSettings::Tag::isdbs3:
+ return settings.get<FrontendSettings::Tag::isdbs3>().frequency;
+ case FrontendSettings::Tag::isdbt:
+ return settings.get<FrontendSettings::Tag::isdbt>().frequency;
+ default:
+ return 0;
+ }
+}
+
+void FrontendCallback::resetBlindScanStartingFrequency(FrontendConfig& config,
+ int32_t resetingFreq) {
+ switch (config.settings.getTag()) {
+ case FrontendSettings::Tag::analog:
+ config.settings.get<FrontendSettings::Tag::analog>().frequency = resetingFreq;
+ break;
+ case FrontendSettings::Tag::atsc:
+ config.settings.get<FrontendSettings::Tag::atsc>().frequency = resetingFreq;
+ break;
+ case FrontendSettings::Tag::atsc3:
+ config.settings.get<FrontendSettings::Tag::atsc3>().frequency = resetingFreq;
+ break;
+ case FrontendSettings::Tag::dvbc:
+ config.settings.get<FrontendSettings::Tag::dvbc>().frequency = resetingFreq;
+ break;
+ case FrontendSettings::Tag::dvbs:
+ config.settings.get<FrontendSettings::Tag::dvbs>().frequency = resetingFreq;
+ break;
+ case FrontendSettings::Tag::dvbt:
+ config.settings.get<FrontendSettings::Tag::dvbt>().frequency = resetingFreq;
+ break;
+ case FrontendSettings::Tag::isdbs:
+ config.settings.get<FrontendSettings::Tag::isdbs>().frequency = resetingFreq;
+ break;
+ case FrontendSettings::Tag::isdbs3:
+ config.settings.get<FrontendSettings::Tag::isdbs3>().frequency = resetingFreq;
+ break;
+ case FrontendSettings::Tag::isdbt:
+ config.settings.get<FrontendSettings::Tag::isdbt>().frequency = resetingFreq;
+ break;
+ default:
+ break;
+ }
+}
+
+AssertionResult FrontendTests::getFrontendIds() {
+ ndk::ScopedAStatus status;
+ status = mService->getFrontendIds(&mFeIds);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FrontendTests::getFrontendInfo(int32_t frontendId) {
+ ndk::ScopedAStatus status;
+ status = mService->getFrontendInfo(frontendId, &mFrontendInfo);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FrontendTests::openFrontendById(int32_t frontendId) {
+ ndk::ScopedAStatus status;
+ status = mService->openFrontendById(frontendId, &mFrontend);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FrontendTests::setFrontendCallback() {
+ EXPECT_TRUE(mFrontend) << "Test with openFrontendById first.";
+ mFrontendCallback = ndk::SharedRefBase::make<FrontendCallback>();
+ auto callbackStatus = mFrontend->setCallback(mFrontendCallback);
+ return AssertionResult(callbackStatus.isOk());
+}
+
+AssertionResult FrontendTests::scanFrontend(FrontendConfig config, FrontendScanType type) {
+ EXPECT_TRUE(mFrontendCallback)
+ << "test with openFrontendById/setFrontendCallback/getFrontendInfo first.";
+
+ EXPECT_TRUE(mFrontendInfo.type == config.type)
+ << "FrontendConfig does not match the frontend info of the given id.";
+
+ mFrontendCallback->scanTest(mFrontend, config, type);
+ return AssertionResult(true);
+}
+
+AssertionResult FrontendTests::stopScanFrontend() {
+ EXPECT_TRUE(mFrontend) << "Test with openFrontendById first.";
+ ndk::ScopedAStatus status;
+ status = mFrontend->stopScan();
+
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FrontendTests::setLnb(int32_t lnbId) {
+ if (!mFrontendCallback) {
+ ALOGW("[vts] open and set frontend callback first.");
+ return failure();
+ }
+ return AssertionResult(mFrontend->setLnb(lnbId).isOk());
+}
+
+AssertionResult FrontendTests::linkCiCam(int32_t ciCamId) {
+ ndk::ScopedAStatus status;
+ int32_t ltsId;
+ status = mFrontend->linkCiCam(ciCamId, <sId);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FrontendTests::unlinkCiCam(int32_t ciCamId) {
+ ndk::ScopedAStatus status = mFrontend->unlinkCiCam(ciCamId);
+ return AssertionResult(status.isOk());
+}
+
+void FrontendTests::verifyFrontendStatus(vector<FrontendStatusType> statusTypes,
+ vector<FrontendStatus> expectStatuses) {
+ ASSERT_TRUE(mFrontend) << "Frontend is not opened yet.";
+ ndk::ScopedAStatus status;
+ vector<FrontendStatus> realStatuses;
+
+ status = mFrontend->getStatus(statusTypes, &realStatuses);
+ ASSERT_TRUE(status.isOk() && realStatuses.size() == statusTypes.size());
+
+ for (int i = 0; i < statusTypes.size(); i++) {
+ FrontendStatusType type = statusTypes[i];
+ switch (type) {
+ case FrontendStatusType::MODULATIONS: {
+ // TODO: verify modulations
+ break;
+ }
+ case FrontendStatusType::BERS: {
+ ASSERT_TRUE(std::equal(realStatuses[i].get<FrontendStatus::Tag::bers>().begin(),
+ realStatuses[i].get<FrontendStatus::Tag::bers>().end(),
+ expectStatuses[i].get<FrontendStatus::Tag::bers>().begin()));
+ break;
+ }
+ case FrontendStatusType::CODERATES: {
+ ASSERT_TRUE(std::equal(
+ realStatuses[i].get<FrontendStatus::Tag::codeRates>().begin(),
+ realStatuses[i].get<FrontendStatus::Tag::codeRates>().end(),
+ expectStatuses[i].get<FrontendStatus::Tag::codeRates>().begin()));
+ break;
+ }
+ case FrontendStatusType::GUARD_INTERVAL: {
+ // TODO: verify interval
+ break;
+ }
+ case FrontendStatusType::TRANSMISSION_MODE: {
+ // TODO: verify tranmission mode
+ break;
+ }
+ case FrontendStatusType::UEC: {
+ ASSERT_TRUE(realStatuses[i].get<FrontendStatus::Tag::uec>() ==
+ expectStatuses[i].get<FrontendStatus::Tag::uec>());
+ break;
+ }
+ case FrontendStatusType::T2_SYSTEM_ID: {
+ ASSERT_TRUE(realStatuses[i].get<FrontendStatus::Tag::systemId>() ==
+ expectStatuses[i].get<FrontendStatus::Tag::systemId>());
+ break;
+ }
+ case FrontendStatusType::INTERLEAVINGS: {
+ ASSERT_TRUE(std::equal(
+ realStatuses[i].get<FrontendStatus::Tag::interleaving>().begin(),
+ realStatuses[i].get<FrontendStatus::Tag::interleaving>().end(),
+ expectStatuses[i].get<FrontendStatus::Tag::interleaving>().begin()));
+ break;
+ }
+ case FrontendStatusType::ISDBT_SEGMENTS: {
+ ASSERT_TRUE(std::equal(
+ realStatuses[i].get<FrontendStatus::Tag::isdbtSegment>().begin(),
+ realStatuses[i].get<FrontendStatus::Tag::isdbtSegment>().end(),
+ expectStatuses[i].get<FrontendStatus::Tag::isdbtSegment>().begin()));
+ break;
+ }
+ case FrontendStatusType::TS_DATA_RATES: {
+ ASSERT_TRUE(std::equal(
+ realStatuses[i].get<FrontendStatus::Tag::tsDataRate>().begin(),
+ realStatuses[i].get<FrontendStatus::Tag::tsDataRate>().end(),
+ expectStatuses[i].get<FrontendStatus::Tag::tsDataRate>().begin()));
+ break;
+ }
+ case FrontendStatusType::ROLL_OFF: {
+ // TODO: verify roll off
+ break;
+ }
+ case FrontendStatusType::IS_MISO: {
+ ASSERT_TRUE(realStatuses[i].get<FrontendStatus::Tag::isMiso>() ==
+ expectStatuses[i].get<FrontendStatus::Tag::isMiso>());
+ break;
+ }
+ case FrontendStatusType::IS_LINEAR: {
+ ASSERT_TRUE(realStatuses[i].get<FrontendStatus::Tag::isLinear>() ==
+ expectStatuses[i].get<FrontendStatus::Tag::isLinear>());
+ break;
+ }
+ case FrontendStatusType::IS_SHORT_FRAMES: {
+ ASSERT_TRUE(realStatuses[i].get<FrontendStatus::Tag::isShortFrames>() ==
+ expectStatuses[i].get<FrontendStatus::Tag::isShortFrames>());
+ break;
+ }
+ default: {
+ continue;
+ }
+ }
+ }
+ ASSERT_TRUE(status.isOk());
+}
+
+AssertionResult FrontendTests::tuneFrontend(FrontendConfig config, bool testWithDemux) {
+ EXPECT_TRUE(mFrontendCallback)
+ << "test with openFrontendById/setFrontendCallback/getFrontendInfo first.";
+
+ EXPECT_TRUE(mFrontendInfo.type == config.type)
+ << "FrontendConfig does not match the frontend info of the given id.";
+
+ mIsSoftwareFe = config.isSoftwareFe;
+ if (mIsSoftwareFe && testWithDemux) {
+ if (getDvrTests()->openDvrInDemux(mDvrConfig.type, mDvrConfig.bufferSize) != success()) {
+ ALOGW("[vts] Software frontend dvr configure openDvr failed.");
+ return failure();
+ }
+ if (getDvrTests()->configDvrPlayback(mDvrConfig.settings) != success()) {
+ ALOGW("[vts] Software frontend dvr configure Dvr playback failed.");
+ return failure();
+ }
+ if (getDvrTests()->getDvrPlaybackMQDescriptor() != success()) {
+ ALOGW("[vts] Software frontend dvr configure get MQDesc failed.");
+ return failure();
+ }
+ getDvrTests()->startPlaybackInputThread(
+ mDvrConfig.playbackInputFile,
+ mDvrConfig.settings.get<DvrSettings::Tag::playback>());
+ }
+ mFrontendCallback->tuneTestOnLock(mFrontend, config.settings);
+ return AssertionResult(true);
+}
+
+AssertionResult FrontendTests::stopTuneFrontend(bool testWithDemux) {
+ EXPECT_TRUE(mFrontend) << "Test with openFrontendById first.";
+ ndk::ScopedAStatus status;
+ status = mFrontend->stopTune();
+ if (mIsSoftwareFe && testWithDemux) {
+ getDvrTests()->stopPlaybackThread();
+ getDvrTests()->closeDvrPlayback();
+ }
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult FrontendTests::closeFrontend() {
+ EXPECT_TRUE(mFrontend) << "Test with openFrontendById first.";
+ ndk::ScopedAStatus status;
+ status = mFrontend->close();
+ mFrontend = nullptr;
+ mFrontendCallback = nullptr;
+ return AssertionResult(status.isOk());
+}
+
+void FrontendTests::getFrontendIdByType(FrontendType feType, int32_t& feId) {
+ ASSERT_TRUE(getFrontendIds());
+ ASSERT_TRUE(mFeIds.size() > 0);
+ for (size_t i = 0; i < mFeIds.size(); i++) {
+ ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
+ if (mFrontendInfo.type != feType) {
+ continue;
+ }
+ feId = mFeIds[i];
+ return;
+ }
+ feId = INVALID_ID;
+}
+
+void FrontendTests::tuneTest(FrontendConfig frontendConf) {
+ int32_t feId;
+ getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(openFrontendById(feId));
+ ASSERT_TRUE(setFrontendCallback());
+ if (frontendConf.canConnectToCiCam) {
+ ASSERT_TRUE(linkCiCam(frontendConf.ciCamId));
+ ASSERT_TRUE(unlinkCiCam(frontendConf.ciCamId));
+ }
+ ASSERT_TRUE(tuneFrontend(frontendConf, false /*testWithDemux*/));
+ verifyFrontendStatus(frontendConf.tuneStatusTypes, frontendConf.expectTuneStatuses);
+ ASSERT_TRUE(stopTuneFrontend(false /*testWithDemux*/));
+ ASSERT_TRUE(closeFrontend());
+}
+
+void FrontendTests::scanTest(FrontendConfig frontendConf, FrontendScanType scanType) {
+ int32_t feId;
+ getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(openFrontendById(feId));
+ ASSERT_TRUE(setFrontendCallback());
+ ASSERT_TRUE(scanFrontend(frontendConf, scanType));
+ ASSERT_TRUE(stopScanFrontend());
+ ASSERT_TRUE(closeFrontend());
+}
diff --git a/tv/tuner/aidl/vts/functional/FrontendTests.h b/tv/tuner/aidl/vts/functional/FrontendTests.h
new file mode 100644
index 0000000..b65704f
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/FrontendTests.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/tv/tuner/BnFrontendCallback.h>
+#include <aidl/android/hardware/tv/tuner/IFrontend.h>
+#include <aidl/android/hardware/tv/tuner/ITuner.h>
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#include "DvrTests.h"
+#include "VtsHalTvTunerTestConfigurations.h"
+
+#define WAIT_TIMEOUT 3000000000
+#define INVALID_ID -1
+
+using android::Condition;
+using android::Mutex;
+
+using ::testing::AssertionResult;
+
+using namespace aidl::android::hardware::tv::tuner;
+using namespace std;
+
+#define INVALID_ID -1
+#define WAIT_TIMEOUT 3000000000
+
+class FrontendCallback : public BnFrontendCallback {
+ public:
+ virtual ndk::ScopedAStatus onEvent(FrontendEventType frontendEventType) override;
+ virtual ndk::ScopedAStatus onScanMessage(FrontendScanMessageType type,
+ const FrontendScanMessage& message) override;
+
+ void tuneTestOnLock(std::shared_ptr<IFrontend>& frontend, FrontendSettings settings);
+ void scanTest(std::shared_ptr<IFrontend>& frontend, FrontendConfig config,
+ FrontendScanType type);
+
+ // Helper methods
+ int32_t getTargetFrequency(FrontendSettings& settings);
+ void resetBlindScanStartingFrequency(FrontendConfig& config, int32_t resetingFreq);
+
+ private:
+ void readFrontendScanMessage_Modulation(FrontendModulation modulation);
+
+ bool mEventReceived = false;
+ bool mScanMessageReceived = false;
+ bool mLockMsgReceived = false;
+ bool mScanMsgProcessed = true;
+ FrontendScanMessageType mScanMessageType;
+ FrontendScanMessage mScanMessage;
+ vector<int8_t> mEventMessage;
+ android::Mutex mMsgLock;
+ android::Condition mMsgCondition;
+ android::Condition mLockMsgCondition;
+};
+
+class FrontendTests {
+ public:
+ void setService(std::shared_ptr<ITuner> tuner) {
+ mService = tuner;
+ getDvrTests()->setService(tuner);
+ getDefaultSoftwareFrontendPlaybackConfig(mDvrConfig);
+ }
+
+ AssertionResult getFrontendIds();
+ AssertionResult getFrontendInfo(int32_t frontendId);
+ AssertionResult openFrontendById(int32_t frontendId);
+ AssertionResult setFrontendCallback();
+ AssertionResult scanFrontend(FrontendConfig config, FrontendScanType type);
+ AssertionResult stopScanFrontend();
+ AssertionResult setLnb(int32_t lnbId);
+ AssertionResult tuneFrontend(FrontendConfig config, bool testWithDemux);
+ void verifyFrontendStatus(vector<FrontendStatusType> statusTypes,
+ vector<FrontendStatus> expectStatuses);
+ AssertionResult stopTuneFrontend(bool testWithDemux);
+ AssertionResult closeFrontend();
+
+ AssertionResult linkCiCam(int32_t ciCamId);
+ AssertionResult unlinkCiCam(int32_t ciCamId);
+
+ void getFrontendIdByType(FrontendType feType, int32_t& feId);
+ void tuneTest(FrontendConfig frontendConf);
+ void scanTest(FrontendConfig frontend, FrontendScanType type);
+
+ void setDvrTests(DvrTests* dvrTests) { mExternalDvrTests = dvrTests; }
+ void setDemux(std::shared_ptr<IDemux> demux) { getDvrTests()->setDemux(demux); }
+ void setSoftwareFrontendDvrConfig(DvrConfig conf) { mDvrConfig = conf; }
+
+ protected:
+ static AssertionResult failure() { return ::testing::AssertionFailure(); }
+ static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+ void getDefaultSoftwareFrontendPlaybackConfig(DvrConfig& dvrConfig) {
+ PlaybackSettings playbackSettings{
+ .statusMask = 0xf,
+ .lowThreshold = 0x1000,
+ .highThreshold = 0x07fff,
+ .dataFormat = DataFormat::ES,
+ .packetSize = static_cast<int8_t>(188),
+ };
+ dvrConfig.type = DvrType::PLAYBACK;
+ dvrConfig.playbackInputFile = "/data/local/tmp/test.es";
+ dvrConfig.bufferSize = FMQ_SIZE_4M;
+ dvrConfig.settings.set<DvrSettings::playback>(playbackSettings);
+ }
+
+ DvrTests* getDvrTests() {
+ return (mExternalDvrTests != nullptr ? mExternalDvrTests : &mDvrTests);
+ }
+
+ std::shared_ptr<ITuner> mService;
+ std::shared_ptr<IFrontend> mFrontend;
+ FrontendInfo mFrontendInfo;
+ std::shared_ptr<FrontendCallback> mFrontendCallback;
+ vector<int32_t> mFeIds;
+
+ DvrTests mDvrTests;
+ DvrTests* mExternalDvrTests = nullptr;
+ bool mIsSoftwareFe = false;
+ DvrConfig mDvrConfig;
+};
diff --git a/tv/tuner/aidl/vts/functional/LnbTests.cpp b/tv/tuner/aidl/vts/functional/LnbTests.cpp
new file mode 100644
index 0000000..d62e58a
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/LnbTests.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 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.
+ */
+
+#include <log/log.h>
+
+#include "LnbTests.h"
+
+ndk::ScopedAStatus LnbCallback::onEvent(LnbEventType lnbEventType) {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ ALOGD("[vts] lnb event received. Type: %d", lnbEventType);
+ mEventReceived = true;
+ mMsgCondition.signal();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus LnbCallback::onDiseqcMessage(const vector<uint8_t>& diseqcMessage) {
+ string msg(diseqcMessage.begin(), diseqcMessage.end());
+ ALOGD("[vts] onDiseqcMessage %s", msg.c_str());
+ return ndk::ScopedAStatus::ok();
+}
+
+AssertionResult LnbTests::getLnbIds(vector<int32_t>& ids) {
+ ndk::ScopedAStatus status;
+ status = mService->getLnbIds(&ids);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult LnbTests::openLnbById(int32_t lnbId) {
+ ndk::ScopedAStatus status;
+ status = mService->openLnbById(lnbId, &mLnb);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult LnbTests::openLnbByName(string lnbName, int32_t& id) {
+ ndk::ScopedAStatus status;
+ vector<int32_t> ids;
+ status = mService->openLnbByName(lnbName, &ids, &mLnb);
+ if (status.isOk()) {
+ id = ids[0];
+ }
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult LnbTests::setLnbCallback() {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ mLnbCallback = ndk::SharedRefBase::make<LnbCallback>();
+ auto callbackStatus = mLnb->setCallback(mLnbCallback);
+ return AssertionResult(callbackStatus.isOk());
+}
+
+AssertionResult LnbTests::setVoltage(LnbVoltage voltage) {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ ndk::ScopedAStatus status = mLnb->setVoltage(voltage);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult LnbTests::setTone(LnbTone tone) {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ ndk::ScopedAStatus status = mLnb->setTone(tone);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult LnbTests::setSatellitePosition(LnbPosition position) {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ ndk::ScopedAStatus status = mLnb->setSatellitePosition(position);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult LnbTests::sendDiseqcMessage(vector<uint8_t> diseqcMsg) {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ ndk::ScopedAStatus status = mLnb->sendDiseqcMessage(diseqcMsg);
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult LnbTests::closeLnb() {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ ndk::ScopedAStatus status = mLnb->close();
+ mLnb = nullptr;
+ mLnbCallback = nullptr;
+ return AssertionResult(status.isOk());
+}
diff --git a/tv/tuner/aidl/vts/functional/LnbTests.h b/tv/tuner/aidl/vts/functional/LnbTests.h
new file mode 100644
index 0000000..d6b5a25
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/LnbTests.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/tv/tuner/BnLnbCallback.h>
+#include <aidl/android/hardware/tv/tuner/ILnb.h>
+#include <aidl/android/hardware/tv/tuner/ITuner.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <map>
+
+using android::Condition;
+using android::Mutex;
+
+using ::testing::AssertionResult;
+
+using namespace aidl::android::hardware::tv::tuner;
+using namespace std;
+
+class LnbCallback : public BnLnbCallback {
+ public:
+ virtual ::ndk::ScopedAStatus onEvent(LnbEventType lnbEventType) override;
+ virtual ::ndk::ScopedAStatus onDiseqcMessage(
+ const std::vector<uint8_t>& diseqcMessage) override;
+
+ private:
+ bool mEventReceived = false;
+ android::Mutex mMsgLock;
+ android::Condition mMsgCondition;
+};
+
+class LnbTests {
+ public:
+ void setService(std::shared_ptr<ITuner> tuner) { mService = tuner; }
+
+ AssertionResult getLnbIds(vector<int32_t>& ids);
+ AssertionResult openLnbById(int32_t lnbId);
+ AssertionResult openLnbByName(string lnbName, int32_t& lnbId);
+ AssertionResult setLnbCallback();
+ AssertionResult setVoltage(LnbVoltage voltage);
+ AssertionResult setTone(LnbTone tone);
+ AssertionResult setSatellitePosition(LnbPosition position);
+ AssertionResult sendDiseqcMessage(vector<uint8_t> diseqcMsg);
+ AssertionResult closeLnb();
+
+ protected:
+ static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+ static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+ std::shared_ptr<ITuner> mService;
+ std::shared_ptr<ILnb> mLnb;
+ std::shared_ptr<LnbCallback> mLnbCallback;
+ vector<int32_t> mLnbIds;
+};
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
new file mode 100644
index 0000000..85e5c68
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -0,0 +1,878 @@
+/*
+ * Copyright 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.
+ */
+
+#include "VtsHalTvTunerTargetTest.h"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+namespace {
+
+AssertionResult TunerBroadcastAidlTest::filterDataOutputTest() {
+ return filterDataOutputTestBase(mFilterTests);
+}
+
+AssertionResult TunerPlaybackAidlTest::filterDataOutputTest() {
+ return filterDataOutputTestBase(mFilterTests);
+}
+
+AssertionResult TunerDescramblerAidlTest::filterDataOutputTest() {
+ return filterDataOutputTestBase(mFilterTests);
+}
+
+void TunerFilterAidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf,
+ FrontendConfig frontendConf) {
+ int32_t feId;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ int64_t filterId;
+
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+ if (filterConf.type.mainType == DemuxFilterMainType::IP) {
+ ASSERT_TRUE(mFilterTests.configIpFilterCid(filterConf.ipCid, filterId));
+ }
+ if (filterConf.monitorEventTypes > 0) {
+ ASSERT_TRUE(mFilterTests.configureMonitorEvent(filterId, filterConf.monitorEventTypes));
+ }
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
+void TunerFilterAidlTest::reconfigSingleFilterInDemuxTest(FilterConfig filterConf,
+ FilterConfig filterReconf,
+ FrontendConfig frontendConf) {
+ int32_t feId;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ int64_t filterId;
+
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDemux(demux);
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterReconf.settings, filterId));
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ ASSERT_TRUE(mFilterTests.startIdTest(filterId));
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
+void TunerFilterAidlTest::testTimeFilter(TimeFilterConfig filterConf) {
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ DemuxCapabilities caps;
+
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.getDemuxCaps(caps));
+ ASSERT_TRUE(caps.bTimeFilter);
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mFilterTests.openTimeFilterInDemux());
+ ASSERT_TRUE(mFilterTests.setTimeStamp(filterConf.timeStamp));
+ ASSERT_TRUE(mFilterTests.getTimeStamp());
+ ASSERT_TRUE(mFilterTests.clearTimeStamp());
+ ASSERT_TRUE(mFilterTests.closeTimeFilter());
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
+void TunerBroadcastAidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
+ FrontendConfig frontendConf) {
+ int32_t feId;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ int64_t filterId;
+
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (mLnbId) {
+ ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
+ }
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDemux(demux);
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ // tune test
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ ASSERT_TRUE(filterDataOutputTest());
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
+void TunerBroadcastAidlTest::broadcastSingleFilterTestWithLnb(FilterConfig filterConf,
+ FrontendConfig frontendConf,
+ LnbConfig lnbConf) {
+ if (lnbConf.name.compare(emptyHardwareId) == 0) {
+ vector<int32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ mLnbId = &ids[0];
+ } else {
+ mLnbId = (int32_t*)malloc(sizeof(int32_t));
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, *mLnbId));
+ }
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConf.position));
+ broadcastSingleFilterTest(filterConf, frontendConf);
+ ASSERT_TRUE(mLnbTests.closeLnb());
+ mLnbId = nullptr;
+}
+
+void TunerBroadcastAidlTest::mediaFilterUsingSharedMemoryTest(FilterConfig filterConf,
+ FrontendConfig frontendConf) {
+ int32_t feId;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ int64_t filterId;
+
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDemux(demux);
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+ ASSERT_TRUE(mFilterTests.getSharedAvMemoryHandle(filterId));
+ ASSERT_TRUE(mFilterTests.configAvFilterStreamType(filterConf.streamType, filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ // tune test
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ ASSERT_TRUE(filterDataOutputTest());
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mFilterTests.releaseShareAvHandle(filterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
+void TunerPlaybackAidlTest::playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf) {
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ int64_t filterId;
+
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ mFilterTests.setDemux(demux);
+ mDvrTests.setDemux(demux);
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
+ mDvrTests.startPlaybackInputThread(dvrConf.playbackInputFile,
+ dvrConf.settings.get<DvrSettings::Tag::playback>());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(filterDataOutputTest());
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ mDvrTests.closeDvrPlayback();
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
+void TunerRecordAidlTest::recordSingleFilterTestWithLnb(FilterConfig filterConf,
+ FrontendConfig frontendConf,
+ DvrConfig dvrConf, LnbConfig lnbConf) {
+ if (lnbConf.name.compare(emptyHardwareId) == 0) {
+ vector<int32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ mLnbId = &ids[0];
+ } else {
+ mLnbId = (int32_t*)malloc(sizeof(int32_t));
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, *mLnbId));
+ }
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConf.position));
+ for (auto msgName : lnbRecord.diseqcMsgs) {
+ ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
+ }
+ recordSingleFilterTest(filterConf, frontendConf, dvrConf);
+ ASSERT_TRUE(mLnbTests.closeLnb());
+ mLnbId = nullptr;
+}
+
+void TunerRecordAidlTest::attachSingleFilterToRecordDvrTest(FilterConfig filterConf,
+ FrontendConfig frontendConf,
+ DvrConfig dvrConf) {
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ mDvrTests.setDemux(demux);
+
+ DvrConfig dvrSourceConfig;
+ if (record.hasFrontendConnection) {
+ int32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ } else {
+ dvrSourceConfig = dvrMap[record.dvrSourceId];
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+
+ int64_t filterId;
+ std::shared_ptr<IFilter> filter;
+ mFilterTests.setDemux(demux);
+
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
+
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
+ filter = mFilterTests.getFilterById(filterId);
+ ASSERT_TRUE(filter != nullptr);
+ ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
+ ASSERT_TRUE(mDvrTests.startDvrRecord());
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mDvrTests.stopDvrRecord());
+ ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ mDvrTests.closeDvrRecord();
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ }
+}
+
+void TunerRecordAidlTest::recordSingleFilterTest(FilterConfig filterConf,
+ FrontendConfig frontendConf, DvrConfig dvrConf) {
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ mDvrTests.setDemux(demux);
+
+ DvrConfig dvrSourceConfig;
+ if (record.hasFrontendConnection) {
+ int32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDvrTests(&mDvrTests);
+ } else {
+ dvrSourceConfig = dvrMap[record.dvrSourceId];
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+
+ int64_t filterId;
+ std::shared_ptr<IFilter> filter;
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
+ filter = mFilterTests.getFilterById(filterId);
+ ASSERT_TRUE(filter != nullptr);
+ mDvrTests.startRecordOutputThread(dvrConf.settings.get<DvrSettings::Tag::record>());
+ ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
+ ASSERT_TRUE(mDvrTests.startDvrRecord());
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ } else {
+ // Start DVR Source
+ mDvrTests.startPlaybackInputThread(
+ dvrSourceConfig.playbackInputFile,
+ dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
+ }
+
+ mDvrTests.testRecordOutput();
+ mDvrTests.stopRecordThread();
+
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ } else {
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+ }
+
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mDvrTests.stopDvrRecord());
+ ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ mDvrTests.closeDvrRecord();
+
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ } else {
+ mDvrTests.closeDvrPlayback();
+ }
+
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
+void TunerDescramblerAidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
+ FrontendConfig frontendConf,
+ DescramblerConfig descConfig) {
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+
+ DvrConfig dvrSourceConfig;
+ if (descrambling.hasFrontendConnection) {
+ int32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDemux(demux);
+ } else {
+ dvrSourceConfig = dvrMap[descrambling.dvrSourceId];
+ mDvrTests.setDemux(demux);
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+
+ set<int64_t> filterIds;
+ int64_t filterId;
+ set<struct FilterConfig>::iterator config;
+ set<int64_t>::iterator id;
+ mFilterTests.setDemux(demux);
+ for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
+ ASSERT_TRUE(mFilterTests.openFilterInDemux((*config).type, (*config).bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+ ASSERT_TRUE(mFilterTests.configFilter((*config).settings, filterId));
+ filterIds.insert(filterId);
+ }
+ ASSERT_TRUE(mDescramblerTests.openDescrambler(demuxId));
+ vector<uint8_t> token;
+ ASSERT_TRUE(mDescramblerTests.getKeyToken(descConfig.casSystemId, descConfig.provisionStr,
+ descConfig.hidlPvtData, token));
+ mDescramblerTests.setKeyToken(token);
+ vector<DemuxPid> pids;
+ DemuxPid pid;
+ for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
+ ASSERT_TRUE(mDescramblerTests.getDemuxPidFromFilterSettings((*config).type,
+ (*config).settings, pid));
+ pids.push_back(pid);
+ ASSERT_TRUE(mDescramblerTests.addPid(pid, nullptr));
+ }
+ for (id = filterIds.begin(); id != filterIds.end(); id++) {
+ ASSERT_TRUE(mFilterTests.startFilter(*id));
+ }
+
+ if (descrambling.hasFrontendConnection) {
+ // tune test
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ } else {
+ // Start DVR Source
+ mDvrTests.startPlaybackInputThread(
+ dvrSourceConfig.playbackInputFile,
+ dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
+ }
+
+ ASSERT_TRUE(filterDataOutputTest());
+
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ } else {
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+ }
+
+ for (id = filterIds.begin(); id != filterIds.end(); id++) {
+ ASSERT_TRUE(mFilterTests.stopFilter(*id));
+ }
+ for (auto pid : pids) {
+ ASSERT_TRUE(mDescramblerTests.removePid(pid, nullptr));
+ }
+ ASSERT_TRUE(mDescramblerTests.closeDescrambler());
+ for (id = filterIds.begin(); id != filterIds.end(); id++) {
+ ASSERT_TRUE(mFilterTests.closeFilter(*id));
+ }
+
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ } else {
+ mDvrTests.closeDvrPlayback();
+ }
+
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
+TEST_P(TunerLnbAidlTest, SendDiseqcMessageToLnb) {
+ description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
+ if (!lnbLive.support) {
+ return;
+ }
+ if (lnbMap[lnbLive.lnbId].name.compare(emptyHardwareId) == 0) {
+ vector<int32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ } else {
+ int32_t id;
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbMap[lnbLive.lnbId].name, id));
+ }
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbMap[lnbLive.lnbId].voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbMap[lnbLive.lnbId].tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbMap[lnbLive.lnbId].position));
+ for (auto msgName : lnbLive.diseqcMsgs) {
+ ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
+ }
+ ASSERT_TRUE(mLnbTests.closeLnb());
+}
+
+TEST_P(TunerDemuxAidlTest, openDemux) {
+ description("Open and close a Demux.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ int32_t feId;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
+TEST_P(TunerDemuxAidlTest, getAvSyncTime) {
+ description("Get the A/V sync time from a PCR filter.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
+ return;
+ }
+ int32_t feId;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ int64_t mediaFilterId;
+ int64_t pcrFilterId;
+ int32_t avSyncHwId;
+ std::shared_ptr<IFilter> mediaFilter;
+
+ mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.videoFilterId].type,
+ filterMap[live.videoFilterId].bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(mediaFilterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.videoFilterId].settings, mediaFilterId));
+ mediaFilter = mFilterTests.getFilterById(mediaFilterId);
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.pcrFilterId].type,
+ filterMap[live.pcrFilterId].bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(pcrFilterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.pcrFilterId].settings, pcrFilterId));
+ ASSERT_TRUE(mDemuxTests.getAvSyncId(mediaFilter, avSyncHwId));
+ ASSERT_TRUE(pcrFilterId == avSyncHwId);
+ ASSERT_TRUE(mDemuxTests.getAvSyncTime(pcrFilterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(pcrFilterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(mediaFilterId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
+TEST_P(TunerFilterAidlTest, StartFilterInDemux) {
+ description("Open and start a filter in Demux.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ // TODO use parameterized tests
+ configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerFilterAidlTest, ConfigIpFilterInDemuxWithCid) {
+ description("Open and configure an ip filter in Demux.");
+ // TODO use parameterized tests
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ if (live.ipFilterId.compare(emptyHardwareId) == 0) {
+ return;
+ }
+ configSingleFilterInDemuxTest(filterMap[live.ipFilterId], frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerFilterAidlTest, ReconfigFilterToReceiveStartId) {
+ description("Recofigure and restart a filter to test start id.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ // TODO use parameterized tests
+ reconfigSingleFilterInDemuxTest(filterMap[live.videoFilterId], filterMap[live.videoFilterId],
+ frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerFilterAidlTest, SetFilterLinkage) {
+ description("Pick up all the possible linkages from the demux caps and set them up.");
+ DemuxCapabilities caps;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.getDemuxCaps(caps));
+ mFilterTests.setDemux(demux);
+ for (int i = 0; i < caps.linkCaps.size(); i++) {
+ uint32_t bitMask = 1;
+ for (int j = 0; j < FILTER_MAIN_TYPE_BIT_COUNT; j++) {
+ if (caps.linkCaps[i] & (bitMask << j)) {
+ int64_t sourceFilterId;
+ int64_t sinkFilterId;
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(getLinkageFilterType(i), FMQ_SIZE_16M));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(sourceFilterId));
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(getLinkageFilterType(j), FMQ_SIZE_16M));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(sinkFilterId));
+ ASSERT_TRUE(mFilterTests.setFilterDataSource(sourceFilterId, sinkFilterId));
+ ASSERT_TRUE(mFilterTests.setFilterDataSourceToDemux(sinkFilterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(sinkFilterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(sourceFilterId));
+ }
+ }
+ }
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
+TEST_P(TunerFilterAidlTest, testTimeFilter) {
+ description("Open a timer filter in Demux and set time stamp.");
+ if (!timeFilter.support) {
+ return;
+ }
+ // TODO use parameterized tests
+ testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
+}
+
+TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsSectionFilterTest) {
+ description("Feed ts data from playback and configure Ts section filter to get output");
+ if (!playback.support || playback.sectionFilterId.compare(emptyHardwareId) == 0) {
+ return;
+ }
+ playbackSingleFilterTest(filterMap[playback.sectionFilterId], dvrMap[playback.dvrId]);
+}
+
+TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsAudioFilterTest) {
+ description("Feed ts data from playback and configure Ts audio filter to get output");
+ if (!playback.support) {
+ return;
+ }
+ playbackSingleFilterTest(filterMap[playback.audioFilterId], dvrMap[playback.dvrId]);
+}
+
+TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsVideoFilterTest) {
+ description("Feed ts data from playback and configure Ts video filter to get output");
+ if (!playback.support) {
+ return;
+ }
+ playbackSingleFilterTest(filterMap[playback.videoFilterId], dvrMap[playback.dvrId]);
+}
+
+TEST_P(TunerRecordAidlTest, RecordDataFlowWithTsRecordFilterTest) {
+ description("Feed ts data from frontend to recording and test with ts record filter");
+ if (!record.support) {
+ return;
+ }
+ recordSingleFilterTest(filterMap[record.recordFilterId], frontendMap[record.frontendId],
+ dvrMap[record.dvrRecordId]);
+}
+
+TEST_P(TunerRecordAidlTest, AttachFiltersToRecordTest) {
+ description("Attach a single filter to the record dvr test.");
+ // TODO use parameterized tests
+ if (!record.support) {
+ return;
+ }
+ attachSingleFilterToRecordDvrTest(filterMap[record.recordFilterId],
+ frontendMap[record.frontendId], dvrMap[record.dvrRecordId]);
+}
+
+TEST_P(TunerRecordAidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
+ description("Feed ts data from Fe with Lnb to recording and test with ts record filter");
+ if (!lnbRecord.support) {
+ return;
+ }
+ recordSingleFilterTestWithLnb(filterMap[lnbRecord.recordFilterId],
+ frontendMap[lnbRecord.frontendId], dvrMap[lnbRecord.dvrRecordId],
+ lnbMap[lnbRecord.lnbId]);
+}
+
+TEST_P(TunerFrontendAidlTest, TuneFrontend) {
+ description("Tune one Frontend with specific setting and check Lock event");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerFrontendAidlTest, AutoScanFrontend) {
+ description("Run an auto frontend scan with specific setting and check lock scanMessage");
+ if (!scan.hasFrontendConnection) {
+ return;
+ }
+ mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_AUTO);
+}
+
+TEST_P(TunerFrontendAidlTest, BlindScanFrontend) {
+ description("Run an blind frontend scan with specific setting and check lock scanMessage");
+ if (!scan.hasFrontendConnection) {
+ return;
+ }
+ mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
+}
+
+TEST_P(TunerFrontendAidlTest, TuneFrontendWithFrontendSettings) {
+ description("Tune one Frontend with setting and check Lock event");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerFrontendAidlTest, BlindScanFrontendWithEndFrequency) {
+ description("Run an blind frontend scan with setting and check lock scanMessage");
+ if (!scan.hasFrontendConnection) {
+ return;
+ }
+ mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
+}
+
+TEST_P(TunerFrontendAidlTest, LinkToCiCam) {
+ description("Test Frontend link to CiCam");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ if (!frontendMap[live.frontendId].canConnectToCiCam) {
+ return;
+ }
+ mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowVideoFilterTest) {
+ description("Test Video Filter functionality in Broadcast use case.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowAudioFilterTest) {
+ description("Test Audio Filter functionality in Broadcast use case.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ broadcastSingleFilterTest(filterMap[live.audioFilterId], frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowSectionFilterTest) {
+ description("Test Section Filter functionality in Broadcast use case.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ if (live.sectionFilterId.compare(emptyHardwareId) == 0) {
+ return;
+ }
+ broadcastSingleFilterTest(filterMap[live.sectionFilterId], frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerBroadcastAidlTest, IonBufferTest) {
+ description("Test the av filter data bufferring.");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerBroadcastAidlTest, LnbBroadcastDataFlowVideoFilterTest) {
+ description("Test Video Filter functionality in Broadcast with Lnb use case.");
+ if (!lnbLive.support) {
+ return;
+ }
+ broadcastSingleFilterTestWithLnb(filterMap[lnbLive.videoFilterId],
+ frontendMap[lnbLive.frontendId], lnbMap[lnbLive.lnbId]);
+}
+
+TEST_P(TunerBroadcastAidlTest, MediaFilterWithSharedMemoryHandle) {
+ description("Test the Media Filter with shared memory handle");
+ if (!live.hasFrontendConnection) {
+ return;
+ }
+ mediaFilterUsingSharedMemoryTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+}
+
+TEST_P(TunerDescramblerAidlTest, CreateDescrambler) {
+ description("Create Descrambler");
+ if (!descrambling.support) {
+ return;
+ }
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+
+ if (descrambling.hasFrontendConnection) {
+ int32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ }
+
+ ASSERT_TRUE(mDescramblerTests.openDescrambler(demuxId));
+ ASSERT_TRUE(mDescramblerTests.closeDescrambler());
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ }
+}
+
+TEST_P(TunerDescramblerAidlTest, ScrambledBroadcastDataFlowMediaFiltersTest) {
+ description("Test ts audio filter in scrambled broadcast use case");
+ if (!descrambling.support) {
+ return;
+ }
+ set<FilterConfig> filterConfs;
+ filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.audioFilterId]));
+ filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.videoFilterId]));
+ scrambledBroadcastTest(filterConfs, frontendMap[descrambling.frontendId],
+ descramblerMap[descrambling.descramblerId]);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TunerBroadcastAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ITuner::descriptor)),
+ android::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TunerFrontendAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ITuner::descriptor)),
+ android::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TunerFilterAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ITuner::descriptor)),
+ android::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TunerRecordAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ITuner::descriptor)),
+ android::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TunerLnbAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ITuner::descriptor)),
+ android::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TunerDemuxAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ITuner::descriptor)),
+ android::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TunerPlaybackAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ITuner::descriptor)),
+ android::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, TunerDescramblerAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ITuner::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace
+
+// Start thread pool to receive callbacks from AIDL service.
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
new file mode 100644
index 0000000..e5cee76
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
@@ -0,0 +1,372 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <android/binder_manager.h>
+
+#include "DemuxTests.h"
+#include "DescramblerTests.h"
+#include "DvrTests.h"
+#include "FrontendTests.h"
+#include "LnbTests.h"
+
+using android::sp;
+
+namespace {
+
+bool initConfiguration() {
+ TunerTestingConfigAidlReader1_0::setConfigFilePath(configFilePath);
+ if (!TunerTestingConfigAidlReader1_0::checkConfigFileExists()) {
+ return false;
+ }
+ initFrontendConfig();
+ initFilterConfig();
+ initDvrConfig();
+ connectHardwaresToTestCases();
+ if (!validateConnections()) {
+ ALOGW("[vts] failed to validate connections.");
+ return false;
+ }
+ return true;
+}
+
+static AssertionResult success() {
+ return ::testing::AssertionSuccess();
+}
+
+AssertionResult filterDataOutputTestBase(FilterTests& tests) {
+ // Data Verify Module
+ std::map<int64_t, std::shared_ptr<FilterCallback>>::iterator it;
+ std::map<int64_t, std::shared_ptr<FilterCallback>> filterCallbacks = tests.getFilterCallbacks();
+ for (it = filterCallbacks.begin(); it != filterCallbacks.end(); it++) {
+ it->second->testFilterDataOutput();
+ }
+ return success();
+}
+
+class TunerLnbAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ mService = ITuner::fromBinder(binder);
+ } else {
+ mService = nullptr;
+ }
+ ASSERT_NE(mService, nullptr);
+ ASSERT_TRUE(initConfiguration());
+
+ mLnbTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ std::shared_ptr<ITuner> mService;
+ LnbTests mLnbTests;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerLnbAidlTest);
+
+class TunerDemuxAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ mService = ITuner::fromBinder(binder);
+ } else {
+ mService = nullptr;
+ }
+ ASSERT_NE(mService, nullptr);
+ ASSERT_TRUE(initConfiguration());
+
+ mFrontendTests.setService(mService);
+ mDemuxTests.setService(mService);
+ mFilterTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ std::shared_ptr<ITuner> mService;
+ FrontendTests mFrontendTests;
+ DemuxTests mDemuxTests;
+ FilterTests mFilterTests;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerDemuxAidlTest);
+
+class TunerFilterAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ mService = ITuner::fromBinder(binder);
+ } else {
+ mService = nullptr;
+ }
+ ASSERT_NE(mService, nullptr);
+ initConfiguration();
+
+ mFrontendTests.setService(mService);
+ mDemuxTests.setService(mService);
+ mFilterTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
+ void reconfigSingleFilterInDemuxTest(FilterConfig filterConf, FilterConfig filterReconf,
+ FrontendConfig frontendConf);
+ void testTimeFilter(TimeFilterConfig filterConf);
+
+ DemuxFilterType getLinkageFilterType(int bit) {
+ DemuxFilterType type;
+ type.mainType = static_cast<DemuxFilterMainType>(1 << bit);
+ switch (type.mainType) {
+ case DemuxFilterMainType::TS:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::UNDEFINED);
+ break;
+ case DemuxFilterMainType::MMTP:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::UNDEFINED);
+ break;
+ case DemuxFilterMainType::IP:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::ipFilterType>(
+ DemuxIpFilterType::UNDEFINED);
+ break;
+ case DemuxFilterMainType::TLV:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tlvFilterType>(
+ DemuxTlvFilterType::UNDEFINED);
+ break;
+ case DemuxFilterMainType::ALP:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::alpFilterType>(
+ DemuxAlpFilterType::UNDEFINED);
+ break;
+ default:
+ break;
+ }
+ return type;
+ }
+ std::shared_ptr<ITuner> mService;
+ FrontendTests mFrontendTests;
+ DemuxTests mDemuxTests;
+ FilterTests mFilterTests;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerFilterAidlTest);
+
+class TunerPlaybackAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ mService = ITuner::fromBinder(binder);
+ } else {
+ mService = nullptr;
+ }
+ ASSERT_NE(mService, nullptr);
+ ASSERT_TRUE(initConfiguration());
+
+ mFrontendTests.setService(mService);
+ mDemuxTests.setService(mService);
+ mFilterTests.setService(mService);
+ mDvrTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ std::shared_ptr<ITuner> mService;
+ FrontendTests mFrontendTests;
+ DemuxTests mDemuxTests;
+ FilterTests mFilterTests;
+ DvrTests mDvrTests;
+
+ AssertionResult filterDataOutputTest();
+
+ void playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf);
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerPlaybackAidlTest);
+
+class TunerRecordAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ mService = ITuner::fromBinder(binder);
+ } else {
+ mService = nullptr;
+ }
+ ASSERT_NE(mService, nullptr);
+ initConfiguration();
+
+ mFrontendTests.setService(mService);
+ mDemuxTests.setService(mService);
+ mFilterTests.setService(mService);
+ mDvrTests.setService(mService);
+ mLnbTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ void attachSingleFilterToRecordDvrTest(FilterConfig filterConf, FrontendConfig frontendConf,
+ DvrConfig dvrConf);
+ void recordSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
+ DvrConfig dvrConf, LnbConfig lnbConf);
+ void recordSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf,
+ DvrConfig dvrConf);
+
+ std::shared_ptr<ITuner> mService;
+ FrontendTests mFrontendTests;
+ DemuxTests mDemuxTests;
+ FilterTests mFilterTests;
+ DvrTests mDvrTests;
+ LnbTests mLnbTests;
+
+ private:
+ int32_t* mLnbId = nullptr;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerRecordAidlTest);
+
+class TunerFrontendAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ mService = ITuner::fromBinder(binder);
+ } else {
+ mService = nullptr;
+ }
+ ASSERT_NE(mService, nullptr);
+ initConfiguration();
+
+ mFrontendTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ std::shared_ptr<ITuner> mService;
+ FrontendTests mFrontendTests;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerFrontendAidlTest);
+
+class TunerBroadcastAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ mService = ITuner::fromBinder(binder);
+ } else {
+ mService = nullptr;
+ }
+ ASSERT_NE(mService, nullptr);
+ initConfiguration();
+
+ mFrontendTests.setService(mService);
+ mDemuxTests.setService(mService);
+ mFilterTests.setService(mService);
+ mLnbTests.setService(mService);
+ mDvrTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ std::shared_ptr<ITuner> mService;
+ FrontendTests mFrontendTests;
+ DemuxTests mDemuxTests;
+ FilterTests mFilterTests;
+ LnbTests mLnbTests;
+ DvrTests mDvrTests;
+
+ AssertionResult filterDataOutputTest();
+
+ void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
+ void broadcastSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
+ LnbConfig lnbConf);
+ void mediaFilterUsingSharedMemoryTest(FilterConfig filterConf, FrontendConfig frontendConf);
+
+ private:
+ int32_t* mLnbId = nullptr;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerBroadcastAidlTest);
+
+class TunerDescramblerAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ if (AServiceManager_isDeclared(GetParam().c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ mService = ITuner::fromBinder(binder);
+ } else {
+ mService = nullptr;
+ }
+ mCasService = IMediaCasService::getService();
+ ASSERT_NE(mService, nullptr);
+ ASSERT_NE(mCasService, nullptr);
+ ASSERT_TRUE(initConfiguration());
+
+ mFrontendTests.setService(mService);
+ mDemuxTests.setService(mService);
+ mDvrTests.setService(mService);
+ mDescramblerTests.setService(mService);
+ mDescramblerTests.setCasService(mCasService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ void scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
+ FrontendConfig frontendConf, DescramblerConfig descConfig);
+ AssertionResult filterDataOutputTest();
+
+ std::shared_ptr<ITuner> mService;
+ android::sp<IMediaCasService> mCasService;
+ FrontendTests mFrontendTests;
+ DemuxTests mDemuxTests;
+ FilterTests mFilterTests;
+ DescramblerTests mDescramblerTests;
+ DvrTests mDvrTests;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerDescramblerAidlTest);
+
+} // namespace
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
new file mode 100644
index 0000000..1ddb641
--- /dev/null
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright 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.
+ */
+
+#include <binder/MemoryDealer.h>
+
+#include "../../../config/TunerTestingConfigAidlReaderV1_0.h"
+
+#include <aidl/android/hardware/tv/tuner/DataFormat.h>
+#include <aidl/android/hardware/tv/tuner/DemuxAlpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterMainType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterMonitorEventType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpAddress.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxMmtpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxRecordScIndexType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxTsFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DvrSettings.h>
+#include <aidl/android/hardware/tv/tuner/DvrType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtBandwidth.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtCoderate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtConstellation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtGuardInterval.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtHierarchy.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtStandard.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtTransmissionMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendType.h>
+#include <aidl/android/hardware/tv/tuner/PlaybackSettings.h>
+#include <aidl/android/hardware/tv/tuner/RecordSettings.h>
+
+using namespace std;
+using namespace aidl::android::hardware::tv::tuner;
+using namespace android::media::tuner::testing::configuration::V1_0;
+
+const int32_t FMQ_SIZE_4M = 0x400000;
+const int32_t FMQ_SIZE_16M = 0x1000000;
+
+const string configFilePath = "/vendor/etc/tuner_vts_config_1_1.xml";
+
+#define FILTER_MAIN_TYPE_BIT_COUNT 5
+
+// Hardware configs
+static map<string, FrontendConfig> frontendMap;
+static map<string, FilterConfig> filterMap;
+static map<string, DvrConfig> dvrMap;
+static map<string, LnbConfig> lnbMap;
+static map<string, TimeFilterConfig> timeFilterMap;
+static map<string, vector<uint8_t>> diseqcMsgMap;
+static map<string, DescramblerConfig> descramblerMap;
+
+// Hardware and test cases connections
+static LiveBroadcastHardwareConnections live;
+static ScanHardwareConnections scan;
+static DvrPlaybackHardwareConnections playback;
+static DvrRecordHardwareConnections record;
+static DescramblingHardwareConnections descrambling;
+static LnbLiveHardwareConnections lnbLive;
+static LnbRecordHardwareConnections lnbRecord;
+static TimeFilterHardwareConnections timeFilter;
+
+/** Config all the frontends that would be used in the tests */
+inline void initFrontendConfig() {
+ // The test will use the internal default fe when default fe is connected to any data flow
+ // without overriding in the xml config.
+ string defaultFeId = "FE_DEFAULT";
+ FrontendDvbtSettings dvbtSettings{
+ .frequency = 578000,
+ .transmissionMode = FrontendDvbtTransmissionMode::AUTO,
+ .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
+ .isHighPriority = true,
+ };
+ frontendMap[defaultFeId].type = FrontendType::DVBT;
+ frontendMap[defaultFeId].settings.set<FrontendSettings::Tag::dvbt>(dvbtSettings);
+
+ vector<FrontendStatusType> types;
+ types.push_back(FrontendStatusType::UEC);
+ types.push_back(FrontendStatusType::IS_MISO);
+
+ vector<FrontendStatus> statuses;
+ FrontendStatus status;
+ status.set<FrontendStatus::Tag::uec>(4);
+ statuses.push_back(status);
+ status.set<FrontendStatus::Tag::isMiso>(true);
+ statuses.push_back(status);
+
+ frontendMap[defaultFeId].tuneStatusTypes = types;
+ frontendMap[defaultFeId].expectTuneStatuses = statuses;
+ frontendMap[defaultFeId].isSoftwareFe = true;
+ frontendMap[defaultFeId].canConnectToCiCam = true;
+ frontendMap[defaultFeId].ciCamId = 0;
+ FrontendDvbtSettings dvbt;
+ dvbt.transmissionMode = FrontendDvbtTransmissionMode::MODE_8K_E;
+ frontendMap[defaultFeId].settings.set<FrontendSettings::Tag::dvbt>(dvbt);
+ // Read customized config
+ TunerTestingConfigAidlReader1_0::readFrontendConfig1_0(frontendMap);
+};
+
+inline void initFilterConfig() {
+ // The test will use the internal default filter when default filter is connected to any
+ // data flow without overriding in the xml config.
+ string defaultAudioFilterId = "FILTER_AUDIO_DEFAULT";
+ string defaultVideoFilterId = "FILTER_VIDEO_DEFAULT";
+
+ filterMap[defaultVideoFilterId].type.mainType = DemuxFilterMainType::TS;
+ filterMap[defaultVideoFilterId]
+ .type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::VIDEO);
+ filterMap[defaultVideoFilterId].bufferSize = FMQ_SIZE_16M;
+ filterMap[defaultVideoFilterId].settings =
+ DemuxFilterSettings::make<DemuxFilterSettings::Tag::ts>();
+ filterMap[defaultVideoFilterId].settings.get<DemuxFilterSettings::Tag::ts>().tpid = 256;
+ DemuxFilterAvSettings video;
+ video.isPassthrough = false;
+ filterMap[defaultVideoFilterId]
+ .settings.get<DemuxFilterSettings::Tag::ts>()
+ .filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::av>(video);
+ filterMap[defaultVideoFilterId].monitorEventTypes =
+ static_cast<int32_t>(DemuxFilterMonitorEventType::SCRAMBLING_STATUS) |
+ static_cast<int32_t>(DemuxFilterMonitorEventType::IP_CID_CHANGE);
+ filterMap[defaultVideoFilterId].streamType.set<AvStreamType::Tag::video>(
+ VideoStreamType::MPEG1);
+
+ filterMap[defaultAudioFilterId].type.mainType = DemuxFilterMainType::TS;
+ filterMap[defaultAudioFilterId]
+ .type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::AUDIO);
+ filterMap[defaultAudioFilterId].bufferSize = FMQ_SIZE_16M;
+ filterMap[defaultAudioFilterId].settings =
+ DemuxFilterSettings::make<DemuxFilterSettings::Tag::ts>();
+ filterMap[defaultAudioFilterId].settings.get<DemuxFilterSettings::Tag::ts>().tpid = 256;
+ DemuxFilterAvSettings audio;
+ audio.isPassthrough = false;
+ filterMap[defaultAudioFilterId]
+ .settings.get<DemuxFilterSettings::Tag::ts>()
+ .filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::av>(audio);
+ filterMap[defaultAudioFilterId].monitorEventTypes =
+ static_cast<int32_t>(DemuxFilterMonitorEventType::SCRAMBLING_STATUS) |
+ static_cast<int32_t>(DemuxFilterMonitorEventType::IP_CID_CHANGE);
+ filterMap[defaultAudioFilterId].streamType.set<AvStreamType::Tag::audio>(AudioStreamType::MP3);
+ // Read customized config
+ TunerTestingConfigAidlReader1_0::readFilterConfig1_0(filterMap);
+};
+
+/** Config all the dvrs that would be used in the tests */
+inline void initDvrConfig() {
+ // Read customized config
+ TunerTestingConfigAidlReader1_0::readDvrConfig1_0(dvrMap);
+};
+
+/** Read the vendor configurations of which hardware to use for each test cases/data flows */
+inline void connectHardwaresToTestCases() {
+ TunerTestingConfigAidlReader1_0::connectLiveBroadcast(live);
+ TunerTestingConfigAidlReader1_0::connectScan(scan);
+ TunerTestingConfigAidlReader1_0::connectDvrRecord(record);
+};
+
+inline bool validateConnections() {
+ if (record.support && !record.hasFrontendConnection &&
+ record.dvrSourceId.compare(emptyHardwareId) == 0) {
+ ALOGW("[vts config] Record must support either a DVR source or a Frontend source.");
+ return false;
+ }
+ bool feIsValid = frontendMap.find(live.frontendId) != frontendMap.end() &&
+ frontendMap.find(scan.frontendId) != frontendMap.end();
+ feIsValid &= record.support ? frontendMap.find(record.frontendId) != frontendMap.end() : true;
+
+ if (!feIsValid) {
+ ALOGW("[vts config] dynamic config fe connection is invalid.");
+ return false;
+ }
+
+ bool dvrIsValid = frontendMap[live.frontendId].isSoftwareFe
+ ? dvrMap.find(live.dvrSoftwareFeId) != dvrMap.end()
+ : true;
+
+ if (record.support) {
+ if (record.hasFrontendConnection) {
+ if (frontendMap[record.frontendId].isSoftwareFe) {
+ dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+ }
+ } else {
+ dvrIsValid &= dvrMap.find(record.dvrSourceId) != dvrMap.end();
+ }
+ dvrIsValid &= dvrMap.find(record.dvrRecordId) != dvrMap.end();
+ }
+
+ if (!dvrIsValid) {
+ ALOGW("[vts config] dynamic config dvr connection is invalid.");
+ return false;
+ }
+
+ bool filterIsValid = filterMap.find(live.audioFilterId) != filterMap.end() &&
+ filterMap.find(live.videoFilterId) != filterMap.end();
+ filterIsValid &=
+ record.support ? filterMap.find(record.recordFilterId) != filterMap.end() : true;
+
+ if (!filterIsValid) {
+ ALOGW("[vts config] dynamic config filter connection is invalid.");
+ return false;
+ }
+
+ return true;
+}
diff --git a/tv/tuner/config/OWNERS b/tv/tuner/config/OWNERS
index 1b3d095..bf2b609 100644
--- a/tv/tuner/config/OWNERS
+++ b/tv/tuner/config/OWNERS
@@ -1,4 +1,3 @@
-nchalko@google.com
-amyjojo@google.com
+hgchen@google.com
shubang@google.com
quxiangfang@google.com
diff --git a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h
new file mode 100644
index 0000000..8525b4f
--- /dev/null
+++ b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h
@@ -0,0 +1,1000 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include <android_media_tuner_testing_configuration_V1_0.h>
+#include <android_media_tuner_testing_configuration_V1_0_enums.h>
+
+#include <aidl/android/hardware/tv/tuner/DataFormat.h>
+#include <aidl/android/hardware/tv/tuner/DemuxAlpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterAvSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterMainType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterRecordSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSectionSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpAddress.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxMmtpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxRecordScIndexType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxTlvFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxTsFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DvrSettings.h>
+#include <aidl/android/hardware/tv/tuner/DvrType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtBandwidth.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtCoderate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtConstellation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtGuardInterval.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtHierarchy.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtPlpMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtStandard.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtTransmissionMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendStatus.h>
+#include <aidl/android/hardware/tv/tuner/FrontendStatusType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendType.h>
+#include <aidl/android/hardware/tv/tuner/LnbPosition.h>
+#include <aidl/android/hardware/tv/tuner/LnbTone.h>
+#include <aidl/android/hardware/tv/tuner/LnbVoltage.h>
+#include <aidl/android/hardware/tv/tuner/PlaybackSettings.h>
+#include <aidl/android/hardware/tv/tuner/RecordSettings.h>
+
+using namespace std;
+using namespace aidl::android::hardware::tv::tuner;
+using namespace android::media::tuner::testing::configuration::V1_0;
+
+const string emptyHardwareId = "";
+
+static string mConfigFilePath;
+
+#define PROVISION_STR \
+ "{ " \
+ " \"id\": 21140844, " \
+ " \"name\": \"Test Title\", " \
+ " \"lowercase_organization_name\": \"Android\", " \
+ " \"asset_key\": { " \
+ " \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\" " \
+ " }, " \
+ " \"cas_type\": 1, " \
+ " \"track_types\": [ ] " \
+ "} "
+
+struct FrontendConfig {
+ bool isSoftwareFe;
+ FrontendType type;
+ bool canConnectToCiCam;
+ int32_t ciCamId;
+ FrontendSettings settings;
+ vector<FrontendStatusType> tuneStatusTypes;
+ vector<FrontendStatus> expectTuneStatuses;
+};
+
+struct FilterConfig {
+ int32_t bufferSize;
+ DemuxFilterType type;
+ DemuxFilterSettings settings;
+ bool getMqDesc;
+ AvStreamType streamType;
+ int32_t ipCid;
+ int32_t monitorEventTypes;
+
+ bool operator<(const FilterConfig& /*c*/) const { return false; }
+};
+
+struct DvrConfig {
+ DvrType type;
+ int32_t bufferSize;
+ DvrSettings settings;
+ string playbackInputFile;
+};
+
+struct LnbConfig {
+ string name;
+ LnbVoltage voltage;
+ LnbTone tone;
+ LnbPosition position;
+};
+
+struct TimeFilterConfig {
+ int64_t timeStamp;
+};
+
+struct DescramblerConfig {
+ int32_t casSystemId;
+ string provisionStr;
+ vector<uint8_t> hidlPvtData;
+};
+
+struct LiveBroadcastHardwareConnections {
+ bool hasFrontendConnection;
+ string frontendId;
+ string dvrSoftwareFeId;
+ string audioFilterId;
+ string videoFilterId;
+ string sectionFilterId;
+ string ipFilterId;
+ string pcrFilterId;
+ /* list string of extra filters; */
+};
+
+struct ScanHardwareConnections {
+ bool hasFrontendConnection;
+ string frontendId;
+};
+
+struct DvrPlaybackHardwareConnections {
+ bool support;
+ string frontendId;
+ string dvrId;
+ string audioFilterId;
+ string videoFilterId;
+ string sectionFilterId;
+ /* list string of extra filters; */
+};
+
+struct DvrRecordHardwareConnections {
+ bool support;
+ bool hasFrontendConnection;
+ string frontendId;
+ string dvrRecordId;
+ string dvrSoftwareFeId;
+ string recordFilterId;
+ string dvrSourceId;
+};
+
+struct DescramblingHardwareConnections {
+ bool support;
+ bool hasFrontendConnection;
+ string frontendId;
+ string dvrSoftwareFeId;
+ string audioFilterId;
+ string videoFilterId;
+ string descramblerId;
+ string dvrSourceId;
+ /* list string of extra filters; */
+};
+
+struct LnbLiveHardwareConnections {
+ bool support;
+ string frontendId;
+ string audioFilterId;
+ string videoFilterId;
+ string lnbId;
+ vector<string> diseqcMsgs;
+ /* list string of extra filters; */
+};
+
+struct LnbRecordHardwareConnections {
+ bool support;
+ string frontendId;
+ string dvrRecordId;
+ string recordFilterId;
+ string lnbId;
+ vector<string> diseqcMsgs;
+ /* list string of extra filters; */
+};
+
+struct TimeFilterHardwareConnections {
+ bool support;
+ string timeFilterId;
+};
+
+struct TunerTestingConfigAidlReader1_0 {
+ public:
+ static void setConfigFilePath(string path) { mConfigFilePath = path; }
+
+ static bool checkConfigFileExists() {
+ auto res = read(mConfigFilePath.c_str());
+ if (res == nullopt) {
+ ALOGW("[ConfigReader] Couldn't read %s."
+ "Please check tuner_testing_dynamic_configuration.xsd"
+ "and sample_tuner_vts_config.xml for more details on how to config Tune VTS.",
+ mConfigFilePath.c_str());
+ }
+ return (res != nullopt);
+ }
+
+ static TunerConfiguration getTunerConfig() { return *read(mConfigFilePath.c_str()); }
+
+ static DataFlowConfiguration getDataFlowConfiguration() {
+ return *getTunerConfig().getFirstDataFlowConfiguration();
+ }
+
+ static HardwareConfiguration getHardwareConfig() {
+ return *getTunerConfig().getFirstHardwareConfiguration();
+ }
+
+ static void readFrontendConfig1_0(map<string, FrontendConfig>& frontendMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasFrontends()) {
+ // TODO: b/182519645 complete the tune status config
+ vector<FrontendStatusType> types;
+ vector<FrontendStatus> statuses;
+
+ types.push_back(FrontendStatusType::DEMOD_LOCK);
+ types.push_back(FrontendStatusType::UEC);
+ types.push_back(FrontendStatusType::IS_MISO);
+
+ FrontendStatus status;
+ status.set<FrontendStatus::Tag::isDemodLocked>(true);
+ statuses.push_back(status);
+ status.set<FrontendStatus::Tag::uec>(4);
+ statuses.push_back(status);
+ status.set<FrontendStatus::Tag::isMiso>(true);
+ statuses.push_back(status);
+
+ auto frontends = *hardwareConfig.getFirstFrontends();
+ for (auto feConfig : frontends.getFrontend()) {
+ string id = feConfig.getId();
+ if (id.compare(string("FE_DEFAULT")) == 0) {
+ // overrid default
+ frontendMap.erase(string("FE_DEFAULT"));
+ }
+ FrontendType type;
+ switch (feConfig.getType()) {
+ case FrontendTypeEnum::UNDEFINED:
+ type = FrontendType::UNDEFINED;
+ break;
+ // TODO: b/182519645 finish all other frontend settings
+ case FrontendTypeEnum::ANALOG:
+ type = FrontendType::ANALOG;
+ break;
+ case FrontendTypeEnum::ATSC:
+ type = FrontendType::ATSC;
+ break;
+ case FrontendTypeEnum::ATSC3:
+ type = FrontendType::ATSC3;
+ break;
+ case FrontendTypeEnum::DVBC:
+ type = FrontendType::DVBC;
+ break;
+ case FrontendTypeEnum::DVBS:
+ type = FrontendType::DVBS;
+ frontendMap[id].settings.set<FrontendSettings::Tag::dvbs>(
+ readDvbsFrontendSettings(feConfig));
+ break;
+ case FrontendTypeEnum::DVBT: {
+ type = FrontendType::DVBT;
+ frontendMap[id].settings.set<FrontendSettings::Tag::dvbt>(
+ readDvbtFrontendSettings(feConfig));
+ break;
+ }
+ case FrontendTypeEnum::ISDBS:
+ type = FrontendType::ISDBS;
+ break;
+ case FrontendTypeEnum::ISDBS3:
+ type = FrontendType::ISDBS3;
+ break;
+ case FrontendTypeEnum::ISDBT:
+ type = FrontendType::ISDBT;
+ break;
+ case FrontendTypeEnum::DTMB:
+ type = FrontendType::DTMB;
+ break;
+ case FrontendTypeEnum::UNKNOWN:
+ ALOGW("[ConfigReader] invalid frontend type");
+ return;
+ default:
+ ALOGW("[ConfigReader] fe already handled in 1_0 reader.");
+ break;
+ }
+ frontendMap[id].type = type;
+ frontendMap[id].isSoftwareFe = feConfig.getIsSoftwareFrontend();
+ // TODO: b/182519645 complete the tune status config
+ frontendMap[id].tuneStatusTypes = types;
+ frontendMap[id].expectTuneStatuses = statuses;
+ getCiCamInfo(feConfig, frontendMap[id].canConnectToCiCam, frontendMap[id].ciCamId);
+ }
+ }
+ }
+
+ static void readFilterConfig1_0(map<string, FilterConfig>& filterMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasFilters()) {
+ auto filters = *hardwareConfig.getFirstFilters();
+ for (auto filterConfig : filters.getFilter()) {
+ string id = filterConfig.getId();
+ if (id.compare(string("FILTER_AUDIO_DEFAULT")) == 0) {
+ // overrid default
+ filterMap.erase(string("FILTER_AUDIO_DEFAULT"));
+ }
+ if (id.compare(string("FILTER_VIDEO_DEFAULT")) == 0) {
+ // overrid default
+ filterMap.erase(string("FILTER_VIDEO_DEFAULT"));
+ }
+
+ DemuxFilterType type;
+ DemuxFilterSettings settings;
+ if (!readFilterTypeAndSettings(filterConfig, type, settings)) {
+ ALOGW("[ConfigReader] invalid filter type");
+ return;
+ }
+ filterMap[id].type = type;
+ filterMap[id].bufferSize = filterConfig.getBufferSize();
+ filterMap[id].getMqDesc = filterConfig.getUseFMQ();
+ filterMap[id].settings = settings;
+
+ if (filterConfig.hasMonitorEventTypes()) {
+ filterMap[id].monitorEventTypes = (uint32_t)filterConfig.getMonitorEventTypes();
+ }
+ if (filterConfig.hasAvFilterSettings_optional()) {
+ auto av = filterConfig.getFirstAvFilterSettings_optional();
+ if (av->hasAudioStreamType_optional()) {
+ filterMap[id].streamType.set<AvStreamType::Tag::audio>(
+ static_cast<AudioStreamType>(av->getAudioStreamType_optional()));
+ }
+ if (av->hasVideoStreamType_optional()) {
+ filterMap[id].streamType.set<AvStreamType::Tag::video>(
+ static_cast<VideoStreamType>(av->getVideoStreamType_optional()));
+ }
+ }
+ if (filterConfig.hasIpFilterConfig_optional()) {
+ auto ip = filterConfig.getFirstIpFilterConfig_optional();
+ if (ip->hasIpCid()) {
+ filterMap[id].ipCid = ip->getIpCid();
+ }
+ }
+ }
+ }
+ }
+
+ static void readDvrConfig1_0(map<string, DvrConfig>& dvrMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasDvrs()) {
+ auto dvrs = *hardwareConfig.getFirstDvrs();
+ for (auto dvrConfig : dvrs.getDvr()) {
+ string id = dvrConfig.getId();
+ DvrType type;
+ switch (dvrConfig.getType()) {
+ case DvrTypeEnum::PLAYBACK:
+ type = DvrType::PLAYBACK;
+ dvrMap[id].settings.set<DvrSettings::Tag::playback>(
+ readPlaybackSettings(dvrConfig));
+ break;
+ case DvrTypeEnum::RECORD:
+ type = DvrType::RECORD;
+ dvrMap[id].settings.set<DvrSettings::Tag::record>(
+ readRecordSettings(dvrConfig));
+ break;
+ case DvrTypeEnum::UNKNOWN:
+ ALOGW("[ConfigReader] invalid DVR type");
+ return;
+ }
+ dvrMap[id].type = type;
+ dvrMap[id].bufferSize = static_cast<uint32_t>(dvrConfig.getBufferSize());
+ if (dvrConfig.hasInputFilePath()) {
+ dvrMap[id].playbackInputFile = dvrConfig.getInputFilePath();
+ }
+ }
+ }
+ }
+
+ static void readLnbConfig1_0(map<string, LnbConfig>& lnbMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasLnbs()) {
+ auto lnbs = *hardwareConfig.getFirstLnbs();
+ for (auto lnbConfig : lnbs.getLnb()) {
+ string id = lnbConfig.getId();
+ if (lnbConfig.hasName()) {
+ lnbMap[id].name = lnbConfig.getName();
+ } else {
+ lnbMap[id].name = emptyHardwareId;
+ }
+ lnbMap[id].voltage = static_cast<LnbVoltage>(lnbConfig.getVoltage());
+ lnbMap[id].tone = static_cast<LnbTone>(lnbConfig.getTone());
+ lnbMap[id].position = static_cast<LnbPosition>(lnbConfig.getPosition());
+ }
+ }
+ }
+
+ static void readDescramblerConfig1_0(map<string, DescramblerConfig>& descramblerMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasDescramblers()) {
+ auto descramblers = *hardwareConfig.getFirstDescramblers();
+ for (auto descramblerConfig : descramblers.getDescrambler()) {
+ string id = descramblerConfig.getId();
+ descramblerMap[id].casSystemId =
+ static_cast<uint32_t>(descramblerConfig.getCasSystemId());
+ if (descramblerConfig.hasProvisionStr()) {
+ descramblerMap[id].provisionStr = descramblerConfig.getProvisionStr();
+ } else {
+ descramblerMap[id].provisionStr = PROVISION_STR;
+ }
+ if (descramblerConfig.hasSesstionPrivatData()) {
+ auto privateData = descramblerConfig.getSesstionPrivatData();
+ int size = privateData.size();
+ descramblerMap[id].hidlPvtData.resize(size);
+ memcpy(descramblerMap[id].hidlPvtData.data(), privateData.data(), size);
+ } else {
+ descramblerMap[id].hidlPvtData.resize(256);
+ }
+ }
+ }
+ }
+
+ static void readDiseqcMessages(map<string, vector<uint8_t>>& diseqcMsgMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasDiseqcMessages()) {
+ auto msgs = *hardwareConfig.getFirstDiseqcMessages();
+ for (auto msgConfig : msgs.getDiseqcMessage()) {
+ string name = msgConfig.getMsgName();
+ for (uint8_t atom : msgConfig.getMsgBody()) {
+ diseqcMsgMap[name].push_back(atom);
+ }
+ }
+ }
+ }
+
+ static void readTimeFilterConfig1_0(map<string, TimeFilterConfig>& timeFilterMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasTimeFilters()) {
+ auto timeFilters = *hardwareConfig.getFirstTimeFilters();
+ for (auto timeFilterConfig : timeFilters.getTimeFilter()) {
+ string id = timeFilterConfig.getId();
+ timeFilterMap[id].timeStamp =
+ static_cast<uint64_t>(timeFilterConfig.getTimeStamp());
+ }
+ }
+ }
+
+ static void connectLiveBroadcast(LiveBroadcastHardwareConnections& live) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasClearLiveBroadcast()) {
+ live.hasFrontendConnection = true;
+ } else {
+ live.hasFrontendConnection = false;
+ return;
+ }
+ auto liveConfig = *dataFlow.getFirstClearLiveBroadcast();
+ live.frontendId = liveConfig.getFrontendConnection();
+
+ live.audioFilterId = liveConfig.getAudioFilterConnection();
+ live.videoFilterId = liveConfig.getVideoFilterConnection();
+ if (liveConfig.hasPcrFilterConnection()) {
+ live.pcrFilterId = liveConfig.getPcrFilterConnection();
+ } else {
+ live.pcrFilterId = emptyHardwareId;
+ }
+ if (liveConfig.hasSectionFilterConnection()) {
+ live.sectionFilterId = liveConfig.getSectionFilterConnection();
+ } else {
+ live.sectionFilterId = emptyHardwareId;
+ }
+ if (liveConfig.hasDvrSoftwareFeConnection()) {
+ live.dvrSoftwareFeId = liveConfig.getDvrSoftwareFeConnection();
+ }
+ if (liveConfig.hasIpFilterConnection()) {
+ live.ipFilterId = liveConfig.getIpFilterConnection();
+ } else {
+ live.ipFilterId = emptyHardwareId;
+ }
+ }
+
+ static void connectScan(ScanHardwareConnections& scan) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasScan()) {
+ scan.hasFrontendConnection = true;
+ } else {
+ scan.hasFrontendConnection = false;
+ return;
+ }
+ auto scanConfig = *dataFlow.getFirstScan();
+ scan.frontendId = scanConfig.getFrontendConnection();
+ }
+
+ static void connectDvrPlayback(DvrPlaybackHardwareConnections& playback) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasDvrPlayback()) {
+ playback.support = true;
+ } else {
+ playback.support = false;
+ return;
+ }
+ auto playbackConfig = *dataFlow.getFirstDvrPlayback();
+ playback.dvrId = playbackConfig.getDvrConnection();
+ playback.audioFilterId = playbackConfig.getAudioFilterConnection();
+ playback.videoFilterId = playbackConfig.getVideoFilterConnection();
+ if (playbackConfig.hasSectionFilterConnection()) {
+ playback.sectionFilterId = playbackConfig.getSectionFilterConnection();
+ } else {
+ playback.sectionFilterId = emptyHardwareId;
+ }
+ }
+
+ static void connectDvrRecord(DvrRecordHardwareConnections& record) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasDvrRecord()) {
+ record.support = true;
+ } else {
+ record.support = false;
+ return;
+ }
+ auto recordConfig = *dataFlow.getFirstDvrRecord();
+ record.recordFilterId = recordConfig.getRecordFilterConnection();
+ record.dvrRecordId = recordConfig.getDvrRecordConnection();
+ if (recordConfig.hasDvrSoftwareFeConnection()) {
+ record.dvrSoftwareFeId = recordConfig.getDvrSoftwareFeConnection();
+ }
+ if (recordConfig.getHasFrontendConnection()) {
+ record.hasFrontendConnection = true;
+ record.dvrSourceId = emptyHardwareId;
+ record.frontendId = recordConfig.getFrontendConnection();
+ } else {
+ record.hasFrontendConnection = false;
+ record.dvrSourceId = recordConfig.getDvrSourceConnection();
+ }
+ }
+
+ static void connectDescrambling(DescramblingHardwareConnections& descrambling) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasDescrambling()) {
+ descrambling.support = true;
+ } else {
+ descrambling.support = false;
+ return;
+ }
+ auto descConfig = *dataFlow.getFirstDescrambling();
+ descrambling.descramblerId = descConfig.getDescramblerConnection();
+ descrambling.audioFilterId = descConfig.getAudioFilterConnection();
+ descrambling.videoFilterId = descConfig.getVideoFilterConnection();
+ if (descConfig.hasDvrSoftwareFeConnection()) {
+ descrambling.dvrSoftwareFeId = descConfig.getDvrSoftwareFeConnection();
+ }
+ if (descConfig.getHasFrontendConnection()) {
+ descrambling.hasFrontendConnection = true;
+ descrambling.dvrSourceId = emptyHardwareId;
+ descrambling.frontendId = descConfig.getFrontendConnection();
+ } else {
+ descrambling.hasFrontendConnection = false;
+ descrambling.dvrSourceId = descConfig.getDvrSourceConnection();
+ }
+ }
+
+ static void connectLnbLive(LnbLiveHardwareConnections& lnbLive) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasLnbLive()) {
+ lnbLive.support = true;
+ } else {
+ lnbLive.support = false;
+ return;
+ }
+ auto lnbLiveConfig = *dataFlow.getFirstLnbLive();
+ lnbLive.frontendId = lnbLiveConfig.getFrontendConnection();
+ lnbLive.audioFilterId = lnbLiveConfig.getAudioFilterConnection();
+ lnbLive.videoFilterId = lnbLiveConfig.getVideoFilterConnection();
+ lnbLive.lnbId = lnbLiveConfig.getLnbConnection();
+ if (lnbLiveConfig.hasDiseqcMsgSender()) {
+ for (auto msgName : lnbLiveConfig.getDiseqcMsgSender()) {
+ lnbLive.diseqcMsgs.push_back(msgName);
+ }
+ }
+ }
+
+ static void connectLnbRecord(LnbRecordHardwareConnections& lnbRecord) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasLnbRecord()) {
+ lnbRecord.support = true;
+ } else {
+ lnbRecord.support = false;
+ return;
+ }
+ auto lnbRecordConfig = *dataFlow.getFirstLnbRecord();
+ lnbRecord.frontendId = lnbRecordConfig.getFrontendConnection();
+ lnbRecord.recordFilterId = lnbRecordConfig.getRecordFilterConnection();
+ lnbRecord.dvrRecordId = lnbRecordConfig.getDvrRecordConnection();
+ lnbRecord.lnbId = lnbRecordConfig.getLnbConnection();
+ if (lnbRecordConfig.hasDiseqcMsgSender()) {
+ for (auto msgName : lnbRecordConfig.getDiseqcMsgSender()) {
+ lnbRecord.diseqcMsgs.push_back(msgName);
+ }
+ }
+ }
+
+ static void connectTimeFilter(TimeFilterHardwareConnections& timeFilter) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasTimeFilter()) {
+ timeFilter.support = true;
+ } else {
+ timeFilter.support = false;
+ return;
+ }
+ auto timeFilterConfig = *dataFlow.getFirstTimeFilter();
+ timeFilter.timeFilterId = timeFilterConfig.getTimeFilterConnection();
+ }
+
+ private:
+ static FrontendDvbtSettings readDvbtFrontendSettings(Frontend feConfig) {
+ ALOGW("[ConfigReader] fe type is dvbt");
+ FrontendDvbtSettings dvbtSettings{
+ .frequency = (int32_t)feConfig.getFrequency(),
+ };
+ if (!feConfig.hasDvbtFrontendSettings_optional()) {
+ ALOGW("[ConfigReader] no more dvbt settings");
+ return dvbtSettings;
+ }
+ auto dvbt = feConfig.getFirstDvbtFrontendSettings_optional();
+ uint32_t trans = static_cast<uint32_t>(dvbt->getTransmissionMode());
+ dvbtSettings.transmissionMode = static_cast<FrontendDvbtTransmissionMode>(trans);
+ dvbtSettings.bandwidth = static_cast<FrontendDvbtBandwidth>(dvbt->getBandwidth());
+ dvbtSettings.isHighPriority = dvbt->getIsHighPriority();
+ dvbtSettings.hierarchy = static_cast<FrontendDvbtHierarchy>(dvbt->getHierarchy());
+ dvbtSettings.hpCoderate = static_cast<FrontendDvbtCoderate>(dvbt->getHpCoderate());
+ dvbtSettings.lpCoderate = static_cast<FrontendDvbtCoderate>(dvbt->getLpCoderate());
+ dvbtSettings.guardInterval =
+ static_cast<FrontendDvbtGuardInterval>(dvbt->getGuardInterval());
+ dvbtSettings.standard = static_cast<FrontendDvbtStandard>(dvbt->getStandard());
+ dvbtSettings.isMiso = dvbt->getIsMiso();
+ dvbtSettings.plpMode = static_cast<FrontendDvbtPlpMode>(dvbt->getPlpMode());
+ dvbtSettings.plpId = dvbt->getPlpId();
+ dvbtSettings.plpGroupId = dvbt->getPlpGroupId();
+ if (dvbt->hasConstellation()) {
+ dvbtSettings.constellation =
+ static_cast<FrontendDvbtConstellation>(dvbt->getConstellation());
+ }
+ return dvbtSettings;
+ }
+
+ static FrontendDvbsSettings readDvbsFrontendSettings(Frontend feConfig) {
+ ALOGW("[ConfigReader] fe type is dvbs");
+ FrontendDvbsSettings dvbsSettings{
+ .frequency = (int32_t)feConfig.getFrequency(),
+ };
+ if (!feConfig.hasDvbsFrontendSettings_optional()) {
+ ALOGW("[ConfigReader] no more dvbs settings");
+ return dvbsSettings;
+ }
+ dvbsSettings.symbolRate = static_cast<uint32_t>(
+ feConfig.getFirstDvbsFrontendSettings_optional()->getSymbolRate());
+ dvbsSettings.inputStreamId = static_cast<uint32_t>(
+ feConfig.getFirstDvbsFrontendSettings_optional()->getInputStreamId());
+ auto dvbs = feConfig.getFirstDvbsFrontendSettings_optional();
+ if (dvbs->hasScanType()) {
+ dvbsSettings.scanType = static_cast<FrontendDvbsScanType>(dvbs->getScanType());
+ }
+ if (dvbs->hasIsDiseqcRxMessage()) {
+ dvbsSettings.isDiseqcRxMessage = dvbs->getIsDiseqcRxMessage();
+ }
+ return dvbsSettings;
+ }
+
+ static bool readFilterTypeAndSettings(Filter filterConfig, DemuxFilterType& type,
+ DemuxFilterSettings& settings) {
+ auto mainType = filterConfig.getMainType();
+ auto subType = filterConfig.getSubType();
+
+ switch (mainType) {
+ case FilterMainTypeEnum::TS: {
+ ALOGW("[ConfigReader] filter main type is ts");
+ type.mainType = DemuxFilterMainType::TS;
+ DemuxTsFilterSettings ts;
+ bool isTsSet = false;
+ switch (subType) {
+ case FilterSubTypeEnum::UNDEFINED:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::UNDEFINED);
+ break;
+ case FilterSubTypeEnum::SECTION:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::SECTION);
+ ts.filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::section>(
+ readSectionFilterSettings(filterConfig));
+ isTsSet = true;
+ break;
+ case FilterSubTypeEnum::PES:
+ // TODO: b/182519645 support all the filter settings
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::PES);
+ break;
+ case FilterSubTypeEnum::TS:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::TS);
+ ts.filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::noinit>(
+ true);
+ isTsSet = true;
+ break;
+ case FilterSubTypeEnum::PCR:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::PCR);
+ ts.filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::noinit>(
+ true);
+ isTsSet = true;
+ break;
+ case FilterSubTypeEnum::TEMI:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::TEMI);
+ ts.filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::noinit>(
+ true);
+ isTsSet = true;
+ break;
+ case FilterSubTypeEnum::AUDIO:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::AUDIO);
+ ts.filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::av>(
+ readAvFilterSettings(filterConfig));
+ isTsSet = true;
+ break;
+ case FilterSubTypeEnum::VIDEO:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::VIDEO);
+ ts.filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::av>(
+ readAvFilterSettings(filterConfig));
+ isTsSet = true;
+ break;
+ case FilterSubTypeEnum::RECORD:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::tsFilterType>(
+ DemuxTsFilterType::RECORD);
+ ts.filterSettings.set<DemuxTsFilterSettingsFilterSettings::Tag::record>(
+ readRecordFilterSettings(filterConfig));
+ isTsSet = true;
+ break;
+ default:
+ ALOGW("[ConfigReader] ts subtype is not supported");
+ return false;
+ }
+ if (filterConfig.hasPid()) {
+ ts.tpid = static_cast<int32_t>(filterConfig.getPid());
+ isTsSet = true;
+ }
+ if (isTsSet) {
+ settings.set<DemuxFilterSettings::Tag::ts>(ts);
+ }
+ break;
+ }
+ case FilterMainTypeEnum::MMTP: {
+ ALOGW("[ConfigReader] filter main type is mmtp");
+ type.mainType = DemuxFilterMainType::MMTP;
+ DemuxMmtpFilterSettings mmtp;
+ bool isMmtpSet = false;
+ switch (subType) {
+ case FilterSubTypeEnum::UNDEFINED:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::UNDEFINED);
+ break;
+ case FilterSubTypeEnum::SECTION:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::SECTION);
+ mmtp.filterSettings
+ .set<DemuxMmtpFilterSettingsFilterSettings::Tag::section>(
+ readSectionFilterSettings(filterConfig));
+ isMmtpSet = true;
+ break;
+ case FilterSubTypeEnum::PES:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::PES);
+ // TODO: b/182519645 support all the filter settings
+ break;
+ case FilterSubTypeEnum::MMTP:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::MMTP);
+ mmtp.filterSettings.set<DemuxMmtpFilterSettingsFilterSettings::Tag::noinit>(
+ true);
+ isMmtpSet = true;
+ break;
+ case FilterSubTypeEnum::AUDIO:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::AUDIO);
+ mmtp.filterSettings.set<DemuxMmtpFilterSettingsFilterSettings::Tag::av>(
+ readAvFilterSettings(filterConfig));
+ isMmtpSet = true;
+ break;
+ case FilterSubTypeEnum::VIDEO:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::VIDEO);
+ mmtp.filterSettings.set<DemuxMmtpFilterSettingsFilterSettings::Tag::av>(
+ readAvFilterSettings(filterConfig));
+ isMmtpSet = true;
+ break;
+ case FilterSubTypeEnum::RECORD:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::RECORD);
+ mmtp.filterSettings.set<DemuxMmtpFilterSettingsFilterSettings::Tag::record>(
+ readRecordFilterSettings(filterConfig));
+ isMmtpSet = true;
+ break;
+ case FilterSubTypeEnum::DOWNLOAD:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::mmtpFilterType>(
+ DemuxMmtpFilterType::DOWNLOAD);
+ // TODO: b/182519645 support all the filter settings
+ break;
+ default:
+ ALOGW("[ConfigReader] mmtp subtype is not supported");
+ return false;
+ }
+ if (filterConfig.hasPid()) {
+ mmtp.mmtpPid = static_cast<int32_t>(filterConfig.getPid());
+ isMmtpSet = true;
+ }
+ if (isMmtpSet) {
+ settings.set<DemuxFilterSettings::Tag::mmtp>(mmtp);
+ }
+ break;
+ }
+ case FilterMainTypeEnum::IP: {
+ ALOGW("[ConfigReader] filter main type is ip");
+ type.mainType = DemuxFilterMainType::IP;
+ DemuxIpFilterSettings ip;
+ switch (subType) {
+ case FilterSubTypeEnum::UNDEFINED:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::ipFilterType>(
+ DemuxIpFilterType::UNDEFINED);
+ break;
+ case FilterSubTypeEnum::SECTION:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::ipFilterType>(
+ DemuxIpFilterType::SECTION);
+ ip.filterSettings.set<DemuxIpFilterSettingsFilterSettings::Tag::section>(
+ readSectionFilterSettings(filterConfig));
+ settings.set<DemuxFilterSettings::Tag::ip>(ip);
+ break;
+ case FilterSubTypeEnum::NTP:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::ipFilterType>(
+ DemuxIpFilterType::NTP);
+ ip.filterSettings.set<DemuxIpFilterSettingsFilterSettings::Tag::noinit>(
+ true);
+ settings.set<DemuxFilterSettings::Tag::ip>(ip);
+ break;
+ case FilterSubTypeEnum::IP: {
+ ip.ipAddr = readIpAddress(filterConfig),
+ ip.filterSettings
+ .set<DemuxIpFilterSettingsFilterSettings::Tag::bPassthrough>(
+ readPassthroughSettings(filterConfig));
+ settings.set<DemuxFilterSettings::Tag::ip>(ip);
+ break;
+ }
+ case FilterSubTypeEnum::IP_PAYLOAD:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::ipFilterType>(
+ DemuxIpFilterType::IP_PAYLOAD);
+ ip.filterSettings.set<DemuxIpFilterSettingsFilterSettings::Tag::noinit>(
+ true);
+ settings.set<DemuxFilterSettings::Tag::ip>(ip);
+ break;
+ case FilterSubTypeEnum::PAYLOAD_THROUGH:
+ type.subType.set<DemuxFilterTypeDemuxFilterSubType::Tag::ipFilterType>(
+ DemuxIpFilterType::PAYLOAD_THROUGH);
+ ip.filterSettings.set<DemuxIpFilterSettingsFilterSettings::Tag::noinit>(
+ true);
+ settings.set<DemuxFilterSettings::Tag::ip>(ip);
+ break;
+ default:
+ ALOGW("[ConfigReader] mmtp subtype is not supported");
+ return false;
+ }
+ break;
+ }
+ default:
+ // TODO: b/182519645 support all the filter configs
+ ALOGW("[ConfigReader] filter main type is not supported in dynamic config");
+ return false;
+ }
+ return true;
+ }
+
+ static DemuxIpAddress readIpAddress(Filter filterConfig) {
+ DemuxIpAddress ipAddress;
+ vector<uint8_t> data;
+ if (!filterConfig.hasIpFilterConfig_optional()) {
+ return ipAddress;
+ }
+ auto ipFilterConfig = filterConfig.getFirstIpFilterConfig_optional();
+ if (ipFilterConfig->hasSrcPort()) {
+ ipAddress.srcPort = ipFilterConfig->getSrcPort();
+ }
+ if (ipFilterConfig->hasDestPort()) {
+ ipAddress.dstPort = ipFilterConfig->getDestPort();
+ }
+ if (ipFilterConfig->getFirstSrcIpAddress()->getIsIpV4()) {
+ data.resize(4);
+ memcpy(data.data(), ipFilterConfig->getFirstSrcIpAddress()->getIp().data(), 4);
+ ipAddress.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(data);
+ } else {
+ data.resize(6);
+ memcpy(data.data(), ipFilterConfig->getFirstSrcIpAddress()->getIp().data(), 6);
+ ipAddress.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(data);
+ }
+ if (ipFilterConfig->getFirstDestIpAddress()->getIsIpV4()) {
+ data.resize(4);
+ memcpy(data.data(), ipFilterConfig->getFirstDestIpAddress()->getIp().data(), 4);
+ ipAddress.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(data);
+ } else {
+ data.resize(6);
+ memcpy(data.data(), ipFilterConfig->getFirstDestIpAddress()->getIp().data(), 6);
+ ipAddress.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(data);
+ }
+ return ipAddress;
+ }
+
+ static bool readPassthroughSettings(Filter filterConfig) {
+ if (!filterConfig.hasIpFilterConfig_optional()) {
+ return false;
+ }
+ auto ipFilterConfig = filterConfig.getFirstIpFilterConfig_optional();
+ if (ipFilterConfig->hasDataPassthrough()) {
+ return ipFilterConfig->getDataPassthrough();
+ }
+ return false;
+ }
+
+ static DemuxFilterSectionSettings readSectionFilterSettings(Filter filterConfig) {
+ DemuxFilterSectionSettings settings;
+ if (!filterConfig.hasSectionFilterSettings_optional()) {
+ return settings;
+ }
+ auto section = filterConfig.getFirstSectionFilterSettings_optional();
+ settings.isCheckCrc = section->getIsCheckCrc();
+ settings.isRepeat = section->getIsRepeat();
+ settings.isRaw = section->getIsRaw();
+ return settings;
+ }
+
+ static DemuxFilterAvSettings readAvFilterSettings(Filter filterConfig) {
+ DemuxFilterAvSettings settings;
+ if (!filterConfig.hasAvFilterSettings_optional()) {
+ return settings;
+ }
+ auto av = filterConfig.getFirstAvFilterSettings_optional();
+ settings.isPassthrough = av->getIsPassthrough();
+ return settings;
+ }
+
+ static DemuxFilterRecordSettings readRecordFilterSettings(Filter filterConfig) {
+ DemuxFilterRecordSettings settings;
+ if (!filterConfig.hasRecordFilterSettings_optional()) {
+ return settings;
+ }
+ auto record = filterConfig.getFirstRecordFilterSettings_optional();
+ settings.tsIndexMask = record->getTsIndexMask();
+ settings.scIndexType = static_cast<DemuxRecordScIndexType>(record->getScIndexType());
+ return settings;
+ }
+
+ static PlaybackSettings readPlaybackSettings(Dvr dvrConfig) {
+ ALOGW("[ConfigReader] dvr type is playback");
+ PlaybackSettings playbackSettings{
+ .statusMask = static_cast<uint8_t>(dvrConfig.getStatusMask()),
+ .lowThreshold = static_cast<int32_t>(dvrConfig.getLowThreshold()),
+ .highThreshold = static_cast<int32_t>(dvrConfig.getHighThreshold()),
+ .dataFormat = static_cast<DataFormat>(dvrConfig.getDataFormat()),
+ .packetSize = static_cast<int8_t>(dvrConfig.getPacketSize()),
+ };
+ return playbackSettings;
+ }
+
+ static RecordSettings readRecordSettings(Dvr dvrConfig) {
+ ALOGW("[ConfigReader] dvr type is record");
+ RecordSettings recordSettings{
+ .statusMask = static_cast<uint8_t>(dvrConfig.getStatusMask()),
+ .lowThreshold = static_cast<int32_t>(dvrConfig.getLowThreshold()),
+ .highThreshold = static_cast<int32_t>(dvrConfig.getHighThreshold()),
+ .dataFormat = static_cast<DataFormat>(dvrConfig.getDataFormat()),
+ .packetSize = static_cast<int8_t>(dvrConfig.getPacketSize()),
+ };
+ return recordSettings;
+ }
+
+ static void getCiCamInfo(Frontend feConfig, bool& canConnectToCiCam, int32_t& ciCamId) {
+ if (!feConfig.hasConnectToCicamId()) {
+ canConnectToCiCam = false;
+ ciCamId = -1;
+ }
+ canConnectToCiCam = true;
+ ciCamId = static_cast<int32_t>(feConfig.getConnectToCicamId());
+ }
+};