Merge "Refine freeBuffer in Mapper 2.0"
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index bec22df..1a9df21 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -378,7 +378,7 @@
}
Return<Result> Device::setConnectedState(const DeviceAddress& address, bool connected) {
- auto key = connected ? AudioParameter::keyStreamConnect : AudioParameter::keyStreamDisconnect;
+ auto key = connected ? AudioParameter::keyDeviceConnect : AudioParameter::keyDeviceDisconnect;
return setParam(key, address);
}
#endif
diff --git a/audio/core/all-versions/default/Stream.cpp b/audio/core/all-versions/default/Stream.cpp
index b995657..e62f6d3 100644
--- a/audio/core/all-versions/default/Stream.cpp
+++ b/audio/core/all-versions/default/Stream.cpp
@@ -243,8 +243,8 @@
Return<Result> Stream::setConnectedState(const DeviceAddress& address, bool connected) {
return setParam(
- connected ? AudioParameter::keyStreamConnect : AudioParameter::keyStreamDisconnect,
- address);
+ connected ? AudioParameter::keyDeviceConnect : AudioParameter::keyDeviceDisconnect,
+ address);
}
#elif MAJOR_VERSION >= 4
Return<void> Stream::getDevices(getDevices_cb _hidl_cb) {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index a46de24..fc441ed 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -671,7 +671,7 @@
.initialValue = {.int32Values = {toInt(VehicleApPowerStateReq::ON), 0}}},
{.config = {.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
- .access = VehiclePropertyAccess::WRITE,
+ .access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE},
.initialValue = {.int32Values = {toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL), 0}}},
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index f6ebcdd..0f20dd1 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -1282,7 +1282,7 @@
*
* @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @access VehiclePropertyAccess:READ_WRITE
*/
AP_POWER_STATE_REPORT = (
0x0A01
diff --git a/boot/1.1/Android.bp b/boot/1.1/Android.bp
new file mode 100644
index 0000000..6a8d57a
--- /dev/null
+++ b/boot/1.1/Android.bp
@@ -0,0 +1,18 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.boot@1.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IBootControl.hal",
+ ],
+ interfaces: [
+ "android.hardware.boot@1.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/boot/1.1/IBootControl.hal b/boot/1.1/IBootControl.hal
new file mode 100644
index 0000000..939dfb3
--- /dev/null
+++ b/boot/1.1/IBootControl.hal
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.boot@1.1;
+
+import @1.0::IBootControl;
+
+interface IBootControl extends @1.0::IBootControl {
+ /**
+ * Sets whether a snapshot-merge of any dynamic partition is in progress.
+ *
+ * After the merge status is set to a given value, subsequent calls to
+ * getSnapshotMergeStatus must return the set value.
+ *
+ * The merge status must be persistent across reboots. That is, getSnapshotMergeStatus
+ * must return the same value after a reboot if the merge status is not altered in any way
+ * (e.g. set by setSnapshotMergeStatus or set to CANCELLED by bootloader).
+ *
+ * Read/write access to the merge status must be atomic. When the HAL is processing a
+ * setSnapshotMergeStatus call, all subsequent calls to getSnapshotMergeStatus must block until
+ * setSnapshotMergeStatus has returned.
+ *
+ * A MERGING state indicates that dynamic partitions are partially comprised by blocks in the
+ * userdata partition.
+ *
+ * When the merge status is set to MERGING, the following operations must be prohibited from the
+ * bootloader:
+ * - Flashing or erasing "userdata" or "metadata".
+ *
+ * The following operations may be prohibited when the status is set to MERGING. If not
+ * prohibited, it is recommended that the user receive a warning.
+ * - Changing the active slot (e.g. via "fastboot set_active")
+ *
+ * @param status Merge status.
+ *
+ * @return success True on success, false otherwise.
+ */
+ setSnapshotMergeStatus(MergeStatus status) generates (bool success);
+
+ /**
+ * Returns whether a snapshot-merge of any dynamic partition is in progress.
+ *
+ * This function must return the merge status set by the last setSnapshotMergeStatus call and
+ * recorded by the bootloader with one exception. If the partitions are being flashed from the
+ * bootloader such that the pending merge must be canceled (for example, if the super partition
+ * is being flashed), this function must return CANCELLED.
+ *
+ * @return success True if the merge status is read successfully, false otherwise.
+ * @return status Merge status.
+ */
+ getSnapshotMergeStatus() generates (MergeStatus status);
+};
+
diff --git a/boot/1.1/types.hal b/boot/1.1/types.hal
new file mode 100644
index 0000000..6346078
--- /dev/null
+++ b/boot/1.1/types.hal
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.boot@1.1;
+
+enum MergeStatus : int32_t {
+ /**
+ * No snapshot or merge is in progress.
+ */
+ NONE = 0,
+
+ /**
+ * The merge status could not be determined.
+ */
+ UNKNOWN,
+
+ /**
+ * Partitions are being snapshotted, but no merge has been started.
+ */
+ SNAPSHOTTED,
+
+ /**
+ * At least one partition has merge is in progress.
+ */
+ MERGING,
+
+ /**
+ * A merge was in progress, but it was canceled by the bootloader.
+ */
+ CANCELLED,
+};
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index be8d7ca..82bb941 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -89,7 +89,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.boot</name>
- <version>1.0</version>
+ <version>1.1</version>
<interface>
<name>IBootControl</name>
<instance>default</instance>
diff --git a/confirmationui/1.0/vts/functional/Android.bp b/confirmationui/1.0/vts/functional/Android.bp
index d19d702..fd088cd 100644
--- a/confirmationui/1.0/vts/functional/Android.bp
+++ b/confirmationui/1.0/vts/functional/Android.bp
@@ -23,7 +23,7 @@
static_libs: [
"android.hardware.confirmationui@1.0",
"android.hardware.keymaster@4.0",
- "libcrypto",
+ "libcrypto_static",
"libcn-cbor",
"android.hardware.confirmationui-support-lib",
],
diff --git a/contexthub/1.0/default/OWNERS b/contexthub/1.0/default/OWNERS
index 5373073..90c2330 100644
--- a/contexthub/1.0/default/OWNERS
+++ b/contexthub/1.0/default/OWNERS
@@ -1,4 +1,3 @@
-aarossig@google.com
arthuri@google.com
bduddie@google.com
-bstack@google.com
+stange@google.com
diff --git a/contexthub/1.0/vts/functional/OWNERS b/contexthub/1.0/vts/functional/OWNERS
index ee01441..045cc4e 100644
--- a/contexthub/1.0/vts/functional/OWNERS
+++ b/contexthub/1.0/vts/functional/OWNERS
@@ -1,8 +1,7 @@
#Context Hub team
-aarossig@google.com
arthuri@google.com
bduddie@google.com
-bstack@google.com
+stange@google.com
#VTS team
yim@google.com
diff --git a/current.txt b/current.txt
index b7383ea..66dbf65 100644
--- a/current.txt
+++ b/current.txt
@@ -573,8 +573,13 @@
# ABI preserving changes to HALs during Android R
b69a7615c508acf5c5201efd1bfa3262167874fc3594e2db5a3ff93addd8ac75 android.hardware.keymaster@4.0::IKeymasterDevice
-ad431c8de51c07934a068e3043d8dd0537ac4d3158627706628b123f42df48dc android.hardware.neuralnetworks@1.0::IPreparedModel
+eb2fa0c883c2185d514be0b84c179b283753ef0c1b77b45b4f359bd23bba8b75 android.hardware.neuralnetworks@1.0::IPreparedModel
fb382e986c10b8fbb797a8546e8f9ea6d1107bfe6f3fb7e57f6bbbf1f807a906 android.hardware.neuralnetworks@1.2::IDevice
-aafcc10cf04ab247e86d4582586c71c6b4c2b8c479241ffa7fe37deb659fc942 android.hardware.neuralnetworks@1.2::IPreparedModel
+40e71cd693de5b832325c5d8f081f2ff20a7ba2b89d401cee5b4b3eb0e241681 android.hardware.neuralnetworks@1.2::IPreparedModel
+a785a57447a81e9c130eef6904c3a5c256076c6a04588c40620ebd6fa2660d77 android.hardware.radio@1.2::types
1a6e2bd289f22931c526b21916910f1d4c436b7acb9556e4243de4ce8e6cc2e4 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback
fd65298e1e09e0e3c781ab18305920d757dbe55a3b459ce17814ec5cf6dfee99 android.hardware.wifi@1.0::IWifiP2pIface
+
+# HALs released in Android R
+07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl
+74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
index d6ebfdd..61d4d58 100644
--- a/drm/1.0/vts/functional/Android.bp
+++ b/drm/1.0/vts/functional/Android.bp
@@ -30,7 +30,7 @@
"libhidlmemory",
"libnativehelper",
"libssl",
- "libcrypto",
+ "libcrypto_static",
],
test_suites: ["general-tests"],
}
diff --git a/drm/1.1/vts/functional/Android.bp b/drm/1.1/vts/functional/Android.bp
index 1090b69..47b02bf 100644
--- a/drm/1.1/vts/functional/Android.bp
+++ b/drm/1.1/vts/functional/Android.bp
@@ -29,7 +29,6 @@
"libhidlmemory",
"libnativehelper",
"libssl",
- "libcrypto",
],
test_suites: ["general-tests"],
}
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
index 6b4a4c0..95883bf 100644
--- a/drm/1.2/vts/functional/Android.bp
+++ b/drm/1.2/vts/functional/Android.bp
@@ -34,7 +34,7 @@
"libhidlmemory",
"libnativehelper",
"libssl",
- "libcrypto",
+ "libcrypto_static",
],
test_suites: ["general-tests"],
}
diff --git a/dumpstate/1.0/default/DumpstateDevice.cpp b/dumpstate/1.0/default/DumpstateDevice.cpp
index 25d92b0..c57bf43 100644
--- a/dumpstate/1.0/default/DumpstateDevice.cpp
+++ b/dumpstate/1.0/default/DumpstateDevice.cpp
@@ -37,11 +37,6 @@
// NOTE: this is just an example on how to use the DumpstateUtil.h functions to implement
// this interface.
- // Exit when dump is completed since this is a lazy HAL.
- addPostCommandTask([]() {
- exit(0);
- });
-
if (handle == nullptr || handle->numFds < 1) {
ALOGE("no FDs\n");
return Void();
diff --git a/dumpstate/1.0/default/service.cpp b/dumpstate/1.0/default/service.cpp
index 4f276b7..76c72b5 100644
--- a/dumpstate/1.0/default/service.cpp
+++ b/dumpstate/1.0/default/service.cpp
@@ -15,22 +15,26 @@
*/
#define LOG_TAG "android.hardware.dumpstate@1.0-service"
+#include <hidl/HidlLazyUtils.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include "DumpstateDevice.h"
-using ::android::hardware::configureRpcThreadpool;
-using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
-using ::android::hardware::dumpstate::V1_0::implementation::DumpstateDevice;
-using ::android::hardware::joinRpcThreadpool;
using ::android::OK;
using ::android::sp;
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::hardware::LazyServiceRegistrar;
+using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using ::android::hardware::dumpstate::V1_0::implementation::DumpstateDevice;
int main(int /* argc */, char* /* argv */ []) {
sp<IDumpstateDevice> dumpstate = new DumpstateDevice;
configureRpcThreadpool(1, true /* will join */);
- if (dumpstate->registerAsService() != OK) {
+
+ auto registrar = LazyServiceRegistrar::getInstance();
+ if (registrar.registerService(dumpstate) != OK) {
ALOGE("Could not register service.");
return 1;
}
diff --git a/graphics/composer/2.1/utils/hwc2on1adapter/Android.bp b/graphics/composer/2.1/utils/hwc2on1adapter/Android.bp
index 062f2e5..0af9745 100644
--- a/graphics/composer/2.1/utils/hwc2on1adapter/Android.bp
+++ b/graphics/composer/2.1/utils/hwc2on1adapter/Android.bp
@@ -55,6 +55,7 @@
"-Wno-shorten-64-to-32",
"-Wno-sign-compare",
"-Wno-missing-prototypes",
+ "-Wno-format-pedantic",
],
srcs: [
diff --git a/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc1Hal.h b/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc1Hal.h
index d9beb4f..db7e67d 100644
--- a/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc1Hal.h
+++ b/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/Gralloc1Hal.h
@@ -282,14 +282,16 @@
}
if (flex.planes[0].component != FLEX_COMPONENT_Y ||
- flex.planes[1].component != FLEX_COMPONENT_Cb ||
- flex.planes[2].component != FLEX_COMPONENT_Cr) {
+ ((flex.planes[1].component != FLEX_COMPONENT_Cb || flex.planes[2].component != FLEX_COMPONENT_Cr) &&
+ (flex.planes[2].component != FLEX_COMPONENT_Cb || flex.planes[1].component != FLEX_COMPONENT_Cr))) {
return false;
}
const auto& y = flex.planes[0];
- const auto& cb = flex.planes[1];
- const auto& cr = flex.planes[2];
+ const auto& cb = (flex.planes[1].component == FLEX_COMPONENT_Cb)?
+ flex.planes[1] : flex.planes[2];
+ const auto& cr = (flex.planes[2].component == FLEX_COMPONENT_Cr)?
+ flex.planes[2] : flex.planes[1];
if (cb.h_increment != cr.h_increment || cb.v_increment != cr.v_increment) {
return false;
diff --git a/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h b/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
index 18fbb6d..8540068 100644
--- a/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
+++ b/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
@@ -37,6 +37,10 @@
Error validateBufferSize(const native_handle_t* bufferHandle,
const IMapper::BufferDescriptorInfo& descriptorInfo,
uint32_t stride) override {
+ if (descriptorInfo.layerCount != 1) {
+ return Error::BAD_VALUE;
+ }
+
if (!mModule->validateBufferSize) {
return Error::NONE;
}
diff --git a/keymaster/3.0/vts/functional/Android.bp b/keymaster/3.0/vts/functional/Android.bp
index b0371c7..69aa56d 100644
--- a/keymaster/3.0/vts/functional/Android.bp
+++ b/keymaster/3.0/vts/functional/Android.bp
@@ -26,7 +26,7 @@
],
static_libs: [
"android.hardware.keymaster@3.0",
- "libcrypto",
+ "libcrypto_static",
"libsoftkeymasterdevice",
],
test_suites: ["general-tests"],
diff --git a/keymaster/4.0/vts/functional/Android.bp b/keymaster/4.0/vts/functional/Android.bp
index 333e408..0401362 100644
--- a/keymaster/4.0/vts/functional/Android.bp
+++ b/keymaster/4.0/vts/functional/Android.bp
@@ -25,7 +25,7 @@
],
static_libs: [
"android.hardware.keymaster@4.0",
- "libcrypto",
+ "libcrypto_static",
"libkeymaster4support",
"libsoftkeymasterdevice",
],
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
index 3af1df3..4838e7e 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
@@ -48,10 +48,11 @@
SecurityLevel KeymasterHidlTest::securityLevel_;
hidl_string KeymasterHidlTest::name_;
hidl_string KeymasterHidlTest::author_;
+string KeymasterHidlTest::service_name_;
-void KeymasterHidlTest::SetUpTestCase() {
- string service_name = KeymasterHidlEnvironment::Instance()->getServiceName<IKeymasterDevice>();
- keymaster_ = ::testing::VtsHalHidlTargetTestBase::getService<IKeymasterDevice>(service_name);
+void KeymasterHidlTest::InitializeKeymaster() {
+ service_name_ = KeymasterHidlEnvironment::Instance()->getServiceName<IKeymasterDevice>();
+ keymaster_ = ::testing::VtsHalHidlTargetTestBase::getService<IKeymasterDevice>(service_name_);
ASSERT_NE(keymaster_, nullptr);
ASSERT_TRUE(keymaster_
@@ -62,18 +63,22 @@
author_ = author;
})
.isOk());
+}
+
+void KeymasterHidlTest::SetUpTestCase() {
+
+ InitializeKeymaster();
os_version_ = ::keymaster::GetOsVersion();
os_patch_level_ = ::keymaster::GetOsPatchlevel();
auto service_manager = android::hidl::manager::V1_0::IServiceManager::getService();
ASSERT_NE(nullptr, service_manager.get());
-
all_keymasters_.push_back(keymaster_);
service_manager->listByInterface(
IKeymasterDevice::descriptor, [&](const hidl_vec<hidl_string>& names) {
for (auto& name : names) {
- if (name == service_name) continue;
+ if (name == service_name_) continue;
auto keymaster =
::testing::VtsHalHidlTargetTestBase::getService<IKeymasterDevice>(name);
ASSERT_NE(keymaster, nullptr);
@@ -269,6 +274,13 @@
return GetCharacteristics(key_blob, client_id, app_data, key_characteristics);
}
+ErrorCode KeymasterHidlTest::GetDebugInfo(DebugInfo* debug_info) {
+ EXPECT_TRUE(keymaster_->getDebugInfo([&](const DebugInfo& hidl_debug_info) {
+ *debug_info = hidl_debug_info;
+ }).isOk());
+ return ErrorCode::OK;
+}
+
ErrorCode KeymasterHidlTest::Begin(KeyPurpose purpose, const HidlBuf& key_blob,
const AuthorizationSet& in_params, AuthorizationSet* out_params,
OperationHandle* op_handle) {
@@ -611,6 +623,20 @@
return ciphertext;
}
+string KeymasterHidlTest::EncryptMessage(const string& message, BlockMode block_mode,
+ PaddingMode padding, uint8_t mac_length_bits,
+ const HidlBuf& iv_in) {
+ SCOPED_TRACE("EncryptMessage");
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(block_mode)
+ .Padding(padding)
+ .Authorization(TAG_MAC_LENGTH, mac_length_bits)
+ .Authorization(TAG_NONCE, iv_in);
+ AuthorizationSet out_params;
+ string ciphertext = EncryptMessage(message, params, &out_params);
+ return ciphertext;
+}
+
string KeymasterHidlTest::DecryptMessage(const HidlBuf& key_blob, const string& ciphertext,
const AuthorizationSet& params) {
SCOPED_TRACE("DecryptMessage");
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.h b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
index 015fc43..b09da45 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.h
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
@@ -37,6 +37,7 @@
using ::android::sp;
using ::std::string;
+using hidl::base::V1_0::DebugInfo;
class HidlBuf : public hidl_vec<uint8_t> {
typedef hidl_vec<uint8_t> super;
@@ -95,6 +96,7 @@
// SetUpTestCase runs only once per test case, not once per test.
static void SetUpTestCase();
+ static void InitializeKeymaster();
static void TearDownTestCase() {
keymaster_.clear();
all_keymasters_.clear();
@@ -140,6 +142,8 @@
const HidlBuf& app_data, KeyCharacteristics* key_characteristics);
ErrorCode GetCharacteristics(const HidlBuf& key_blob, KeyCharacteristics* key_characteristics);
+ ErrorCode GetDebugInfo(DebugInfo* debug_info);
+
ErrorCode Begin(KeyPurpose purpose, const HidlBuf& key_blob, const AuthorizationSet& in_params,
AuthorizationSet* out_params, OperationHandle* op_handle);
ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
@@ -201,6 +205,8 @@
HidlBuf* iv_out);
string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
const HidlBuf& iv_in);
+ string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+ uint8_t mac_length_bits, const HidlBuf& iv_in);
string DecryptMessage(const HidlBuf& key_blob, const string& ciphertext,
const AuthorizationSet& params);
@@ -235,6 +241,7 @@
static SecurityLevel securityLevel_;
static hidl_string name_;
static hidl_string author_;
+ static string service_name_;
};
} // namespace test
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index 9e6cce7..0ac7e48 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -18,6 +18,7 @@
#include <cutils/log.h>
#include <iostream>
+#include <signal.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
@@ -2706,6 +2707,40 @@
}
/*
+ * EncryptionOperationsTest.AesWrongPurpose
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized purpose is specified.
+ */
+TEST_F(EncryptionOperationsTest, AesWrongPurpose) {
+ auto err = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesKey(128)
+ .Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)
+ .Padding(PaddingMode::NONE));
+ ASSERT_EQ(ErrorCode::OK, err) << "Got " << err;
+
+ err = Begin(KeyPurpose::DECRYPT,
+ AuthorizationSetBuilder().BlockMode(BlockMode::GCM).Padding(PaddingMode::NONE));
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
+
+ CheckedDeleteKey();
+
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesKey(128)
+ .Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)
+ .Padding(PaddingMode::NONE)));
+
+ err = Begin(KeyPurpose::ENCRYPT,
+ AuthorizationSetBuilder().BlockMode(BlockMode::GCM).Padding(PaddingMode::NONE));
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
+}
+
+/*
* EncryptionOperationsTest.AesEcbNoPaddingWrongInputSize
*
* Verifies that AES encryption fails in the correct way when provided an input that is not a
@@ -3225,6 +3260,92 @@
}
/*
+ * EncryptionOperationsTest.AesGcmRoundTripWithDelaySuccess
+ *
+ * Verifies that AES GCM mode works, even when there's a long delay
+ * between operations.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmRoundTripWithDelaySuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+
+ auto begin_params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+
+ auto update_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params))
+ << "Begin encrypt";
+ string ciphertext;
+ AuthorizationSet update_out_params;
+ sleep(5);
+ ASSERT_EQ(ErrorCode::OK,
+ Finish(op_handle_, update_params, message, "", &update_out_params, &ciphertext));
+
+ ASSERT_EQ(ciphertext.length(), message.length() + 16);
+
+ // Grab nonce
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt.
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
+ string plaintext;
+ size_t input_consumed;
+ sleep(5);
+ ASSERT_EQ(ErrorCode::OK, Update(op_handle_, update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ sleep(5);
+ EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext));
+ EXPECT_EQ(message.length(), plaintext.length());
+ EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmDifferentNonces
+ *
+ * Verifies that encrypting the same data with different nonces produces different outputs.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmDifferentNonces) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)
+ .Authorization(TAG_CALLER_NONCE)));
+
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ string nonce1 = "000000000000";
+ string nonce2 = "111111111111";
+ string nonce3 = "222222222222";
+
+ string ciphertext1 =
+ EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, HidlBuf(nonce1));
+ string ciphertext2 =
+ EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, HidlBuf(nonce2));
+ string ciphertext3 =
+ EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, HidlBuf(nonce3));
+
+ ASSERT_NE(ciphertext1, ciphertext2);
+ ASSERT_NE(ciphertext1, ciphertext3);
+ ASSERT_NE(ciphertext2, ciphertext3);
+}
+
+/*
* EncryptionOperationsTest.AesGcmTooShortTag
*
* Verifies that AES GCM mode fails correctly when a too-short tag length is specified.
@@ -4456,6 +4577,84 @@
EXPECT_EQ(result, std::make_pair(ErrorCode::OK, HidlBuf()));
}
+
+using ClearOperationsTest = KeymasterHidlTest;
+
+/*
+ * ClearSlotsTest.TooManyOperations
+ *
+ * Verifies that TOO_MANY_OPERATIONS is returned after the max number of
+ * operations are started without being finished or aborted. Also verifies
+ * that aborting the operations clears the operations.
+ *
+ */
+TEST_F(ClearOperationsTest, TooManyOperations) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(2048, 65537)
+ .Padding(PaddingMode::NONE)));
+
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+ int max_operations = SecLevel() == SecurityLevel::STRONGBOX ? 4 : 16;
+ OperationHandle op_handles[max_operations];
+ AuthorizationSet out_params;
+ for(int i=0; i<max_operations; i++) {
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &(op_handles[i])));
+ }
+ EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
+ Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
+ // Try again just in case there's a weird overflow bug
+ EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
+ Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
+ for(int i=0; i<max_operations; i++) {
+ EXPECT_EQ(ErrorCode::OK, Abort(op_handles[i]));
+ }
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
+ AbortIfNeeded();
+}
+
+/*
+ * ClearSlotsTest.ServiceDeath
+ *
+ * Verifies that the service is restarted after death and the ongoing
+ * operations are cleared.
+ */
+TEST_F(ClearOperationsTest, ServiceDeath) {
+
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(2048, 65537)
+ .Padding(PaddingMode::NONE)));
+
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+ int max_operations = SecLevel() == SecurityLevel::STRONGBOX ? 4 : 16;
+ OperationHandle op_handles[max_operations];
+ AuthorizationSet out_params;
+ for(int i=0; i<max_operations; i++) {
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &(op_handles[i])));
+ }
+ EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
+ Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
+
+ DebugInfo debug_info;
+ GetDebugInfo(&debug_info);
+ kill(debug_info.pid, SIGKILL);
+ // wait 1 second for keymaster to restart
+ sleep(1);
+ InitializeKeymaster();
+
+ for(int i=0; i<max_operations; i++) {
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &(op_handles[i])));
+ }
+ EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
+ Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, &op_handle_));
+ for(int i=0; i<max_operations; i++) {
+ EXPECT_EQ(ErrorCode::OK, Abort(op_handles[i]));
+ }
+}
+
+
} // namespace test
} // namespace V4_0
} // namespace keymaster
diff --git a/light/utils/main.cpp b/light/utils/main.cpp
index d07e799..b834132 100644
--- a/light/utils/main.cpp
+++ b/light/utils/main.cpp
@@ -25,7 +25,7 @@
std::cerr << msg << std::endl;
}
-int main() {
+int main(int argc, char* argv[]) {
using ::android::hardware::hidl_vec;
using ::android::hardware::light::V2_0::Brightness;
using ::android::hardware::light::V2_0::Flash;
@@ -41,10 +41,29 @@
return -1;
}
- const static LightState off = {
- .color = 0u, .flashMode = Flash::NONE, .brightnessMode = Brightness::USER,
+ static LightState off = {
+ .color = 0u,
+ .flashMode = Flash::NONE,
+ .brightnessMode = Brightness::USER,
};
+ if (argc > 2) {
+ error("Usage: blank_screen [color]");
+ return -1;
+ }
+
+ if (argc > 1) {
+ char* col_ptr;
+ unsigned int col_new;
+
+ col_new = strtoul(argv[1], &col_ptr, 0);
+ if (*col_ptr != '\0') {
+ error("Failed to convert " + std::string(argv[1]) + " to number");
+ return -1;
+ }
+ off.color = col_new;
+ }
+
service->getSupportedTypes([&](const hidl_vec<Type>& types) {
for (Type type : types) {
Status ret = service->setLight(type, off);
diff --git a/neuralnetworks/1.0/IPreparedModel.hal b/neuralnetworks/1.0/IPreparedModel.hal
index 5320050..3dc3202 100644
--- a/neuralnetworks/1.0/IPreparedModel.hal
+++ b/neuralnetworks/1.0/IPreparedModel.hal
@@ -49,11 +49,14 @@
* must not change the content of any of the data objects corresponding to
* 'request' inputs.
*
- * If the prepared model was prepared from a model wherein all
- * tensor operands have fully specified dimensions, and the inputs
- * to the function are valid, then the execution should launch
- * and complete successfully (ErrorStatus::NONE). There must be
- * no failure unless the device itself is in a bad state.
+ * If the prepared model was prepared from a model wherein all tensor
+ * operands have fully specified dimensions, and the inputs to the function
+ * are valid, then:
+ * - the execution should launch successfully (ErrorStatus::NONE): There
+ * must be no failure unless the device itself is in a bad state.
+ * - if at execution time every operation's input operands have legal
+ * values, the execution should complete successfully (ErrorStatus::NONE):
+ * There must be no failure unless the device itself is in a bad state.
*
* Multiple threads can call the execute function on the same IPreparedModel
* object concurrently with different requests.
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index 0af7f79..3e9d5f7 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -40,10 +40,11 @@
],
}
-cc_defaults {
- name: "VtsHalNeuralNetworksV1_0TargetTestDefaults",
+cc_test {
+ name: "VtsHalNeuralnetworksV1_0TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
+ "BasicTests.cpp",
"TestAssertions.cpp",
"ValidateModel.cpp",
"ValidateRequest.cpp",
@@ -64,33 +65,11 @@
"libneuralnetworks_utils",
"VtsHalNeuralNetworksV1_0_utils",
],
+ whole_static_libs: [
+ "neuralnetworks_generated_V1_0_example",
+ ],
header_libs: [
"libneuralnetworks_headers",
],
test_suites: ["general-tests"],
}
-
-cc_test {
- name: "VtsHalNeuralnetworksV1_0TargetTest",
- defaults: ["VtsHalNeuralNetworksV1_0TargetTestDefaults"],
- srcs: [
- "BasicTests.cpp",
- ],
- whole_static_libs: [
- "neuralnetworks_generated_V1_0_example",
- ],
-}
-
-cc_test {
- name: "PresubmitHalNeuralnetworksV1_0TargetTest",
- defaults: ["VtsHalNeuralNetworksV1_0TargetTestDefaults"],
- srcs: [
- "BasicTests.cpp",
- ],
- whole_static_libs: [
- "neuralnetworks_generated_V1_0_example",
- ],
- cflags: [
- "-DPRESUBMIT_NOT_VTS",
- ],
-}
diff --git a/neuralnetworks/1.0/vts/functional/BasicTests.cpp b/neuralnetworks/1.0/vts/functional/BasicTests.cpp
index 5727ca4..cc44c9e 100644
--- a/neuralnetworks/1.0/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.0/vts/functional/BasicTests.cpp
@@ -21,19 +21,19 @@
namespace android::hardware::neuralnetworks::V1_0::vts::functional {
// create device test
-TEST_F(NeuralnetworksHidlTest, CreateDevice) {}
+TEST_P(NeuralnetworksHidlTest, CreateDevice) {}
// status test
-TEST_F(NeuralnetworksHidlTest, StatusTest) {
- Return<DeviceStatus> status = device->getStatus();
+TEST_P(NeuralnetworksHidlTest, StatusTest) {
+ Return<DeviceStatus> status = kDevice->getStatus();
ASSERT_TRUE(status.isOk());
EXPECT_EQ(DeviceStatus::AVAILABLE, static_cast<DeviceStatus>(status));
}
// initialization
-TEST_F(NeuralnetworksHidlTest, GetCapabilitiesTest) {
+TEST_P(NeuralnetworksHidlTest, GetCapabilitiesTest) {
Return<void> ret =
- device->getCapabilities([](ErrorStatus status, const Capabilities& capabilities) {
+ kDevice->getCapabilities([](ErrorStatus status, const Capabilities& capabilities) {
EXPECT_EQ(ErrorStatus::NONE, status);
EXPECT_LT(0.0f, capabilities.float32Performance.execTime);
EXPECT_LT(0.0f, capabilities.float32Performance.powerUsage);
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index 33a6fa5..595ad85 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -122,9 +122,15 @@
// Top level driver for models and examples generated by test_generator.py
// Test driver for those generated from ml/nn/runtime/test/spec
-void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestModel& testModel) {
+void Execute(const sp<IDevice>& device, const TestModel& testModel) {
+ const Model model = createModel(testModel);
const Request request = createRequest(testModel);
+ // Create IPreparedModel.
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
+
// Launch execution.
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
Return<ErrorStatus> executionLaunchStatus = preparedModel->execute(request, executionCallback);
@@ -142,54 +148,25 @@
checkResults(testModel, outputs);
}
+void GeneratedTestBase::SetUp() {
+ testing::TestWithParam<GeneratedTestParam>::SetUp();
+ ASSERT_NE(kDevice, nullptr);
+}
+
+std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
+ return TestModelManager::get().getTestModels(filter);
+}
+
+std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info) {
+ const auto& [namedDevice, namedModel] = info.param;
+ return gtestCompliantName(getName(namedDevice) + "_" + getName(namedModel));
+}
+
// Tag for the generated tests
-class GeneratedTest : public GeneratedTestBase {
- protected:
- void Execute(const TestModel& testModel) {
- Model model = createModel(testModel);
-
- // see if service can handle model
- bool fullySupportsModel = false;
- Return<void> supportedCall = device->getSupportedOperations(
- model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
- ASSERT_EQ(ErrorStatus::NONE, status);
- ASSERT_NE(0ul, supported.size());
- fullySupportsModel = std::all_of(supported.begin(), supported.end(),
- [](bool valid) { return valid; });
- });
- ASSERT_TRUE(supportedCall.isOk());
-
- // launch prepare model
- sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus =
- device->prepareModel(model, preparedModelCallback);
- ASSERT_TRUE(prepareLaunchStatus.isOk());
- ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
-
- // retrieve prepared model
- preparedModelCallback->wait();
- ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
- sp<IPreparedModel> preparedModel = preparedModelCallback->getPreparedModel();
-
- // early termination if vendor service cannot fully prepare model
- if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
- ASSERT_EQ(nullptr, preparedModel.get());
- LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
- "prepare model that it does not support.";
- std::cout << "[ ] Early termination of test because vendor service cannot "
- "prepare model that it does not support."
- << std::endl;
- GTEST_SKIP();
- }
- EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
- ASSERT_NE(nullptr, preparedModel.get());
-
- EvaluatePreparedModel(preparedModel, testModel);
- }
-};
+class GeneratedTest : public GeneratedTestBase {};
TEST_P(GeneratedTest, Test) {
- Execute(*mTestModel);
+ Execute(kDevice, kTestModel);
}
INSTANTIATE_GENERATED_TEST(GeneratedTest,
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
index a42f271..f230a02 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
@@ -18,41 +18,38 @@
#define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_GENERATED_TEST_HARNESS_H
#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <functional>
#include "TestHarness.h"
#include "VtsHalNeuralnetworks.h"
namespace android::hardware::neuralnetworks::V1_0::vts::functional {
-class GeneratedTestBase
- : public NeuralnetworksHidlTest,
- public ::testing::WithParamInterface<test_helper::TestModelManager::TestParam> {
- protected:
- void SetUp() override {
- NeuralnetworksHidlTest::SetUp();
- ASSERT_NE(mTestModel, nullptr);
- }
+using NamedModel = Named<const test_helper::TestModel*>;
+using GeneratedTestParam = std::tuple<NamedDevice, NamedModel>;
- const test_helper::TestModel* mTestModel = GetParam().second;
+class GeneratedTestBase : public testing::TestWithParam<GeneratedTestParam> {
+ protected:
+ void SetUp() override;
+ const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
+ const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
};
-#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
- INSTANTIATE_TEST_SUITE_P( \
- TestGenerated, TestSuite, \
- ::testing::ValuesIn(::test_helper::TestModelManager::get().getTestModels(filter)), \
- [](const auto& info) { return info.param.first; })
+using FilterFn = std::function<bool(const test_helper::TestModel&)>;
+std::vector<NamedModel> getNamedModels(const FilterFn& filter);
+
+std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
+
+#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
+ INSTANTIATE_TEST_SUITE_P(TestGenerated, TestSuite, \
+ testing::Combine(testing::ValuesIn(getNamedDevices()), \
+ testing::ValuesIn(getNamedModels(filter))), \
+ printGeneratedTest)
// Tag for the validation tests, instantiated in VtsHalNeuralnetworks.cpp.
// TODO: Clean up the hierarchy for ValidationTest.
-class ValidationTest : public GeneratedTestBase {
- protected:
- void validateEverything(const Model& model, const Request& request);
+class ValidationTest : public GeneratedTestBase {};
- private:
- void validateModel(const Model& model);
- void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request);
-};
-
-Model createModel(const ::test_helper::TestModel& testModel);
+Model createModel(const test_helper::TestModel& testModel);
} // namespace android::hardware::neuralnetworks::V1_0::vts::functional
diff --git a/neuralnetworks/1.0/vts/functional/Utils.cpp b/neuralnetworks/1.0/vts/functional/Utils.cpp
index 5de99fd..5b630fd 100644
--- a/neuralnetworks/1.0/vts/functional/Utils.cpp
+++ b/neuralnetworks/1.0/vts/functional/Utils.cpp
@@ -26,6 +26,7 @@
#include <hidlmemory/mapping.h>
#include <algorithm>
+#include <iostream>
#include <vector>
namespace android::hardware::neuralnetworks {
@@ -116,4 +117,23 @@
return outputBuffers;
}
+std::string gtestCompliantName(std::string name) {
+ // gtest test names must only contain alphanumeric characters
+ std::replace_if(
+ name.begin(), name.end(), [](char c) { return !std::isalnum(c); }, '_');
+ return name;
+}
+
} // namespace android::hardware::neuralnetworks
+
+namespace android::hardware::neuralnetworks::V1_0 {
+
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
+ return os << toString(errorStatus);
+}
+
+::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus) {
+ return os << toString(deviceStatus);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_0
diff --git a/neuralnetworks/1.0/vts/functional/ValidateModel.cpp b/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
index 9854395..cc15263 100644
--- a/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
@@ -27,7 +27,7 @@
///////////////////////// UTILITY FUNCTIONS /////////////////////////
static void validateGetSupportedOperations(const sp<IDevice>& device, const std::string& message,
- const V1_0::Model& model) {
+ const Model& model) {
SCOPED_TRACE(message + " [getSupportedOperations]");
Return<void> ret =
@@ -38,7 +38,7 @@
}
static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
- const V1_0::Model& model) {
+ const Model& model) {
SCOPED_TRACE(message + " [prepareModel]");
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
@@ -57,7 +57,7 @@
// mutation to it to invalidate the model, then pass it to interface calls that
// use the model. Note that the model here is passed by value, and any mutation
// to the model does not leave this function.
-static void validate(const sp<IDevice>& device, const std::string& message, V1_0::Model model,
+static void validate(const sp<IDevice>& device, const std::string& message, Model model,
const std::function<void(Model*)>& mutation) {
mutation(&model);
validateGetSupportedOperations(device, message, model);
@@ -113,7 +113,7 @@
static_cast<int32_t>(OperandType::TENSOR_OEM_BYTE) + 1, // upper bound OEM
};
-static void mutateOperandTypeTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void mutateOperandTypeTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
for (int32_t invalidOperandType : invalidOperandTypes) {
const std::string message = "mutateOperandTypeTest: operand " +
@@ -143,7 +143,7 @@
}
}
-static void mutateOperandRankTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void mutateOperandRankTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
const uint32_t invalidRank = getInvalidRank(model.operands[operand].type);
const std::string message = "mutateOperandRankTest: operand " + std::to_string(operand) +
@@ -172,7 +172,7 @@
}
}
-static void mutateOperandScaleTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void mutateOperandScaleTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
const float invalidScale = getInvalidScale(model.operands[operand].type);
const std::string message = "mutateOperandScaleTest: operand " + std::to_string(operand) +
@@ -200,7 +200,7 @@
}
}
-static void mutateOperandZeroPointTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void mutateOperandZeroPointTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
const std::vector<int32_t> invalidZeroPoints =
getInvalidZeroPoints(model.operands[operand].type);
@@ -257,7 +257,7 @@
*operand = newOperand;
}
-static bool mutateOperationOperandTypeSkip(size_t operand, const V1_0::Model& model) {
+static bool mutateOperationOperandTypeSkip(size_t operand, const Model& model) {
// LSH_PROJECTION's second argument is allowed to have any type. This is the
// only operation that currently has a type that can be anything independent
// from any other type. Changing the operand type to any other type will
@@ -271,7 +271,7 @@
return false;
}
-static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
if (mutateOperationOperandTypeSkip(operand, model)) {
continue;
@@ -302,7 +302,7 @@
static_cast<int32_t>(OperationType::OEM_OPERATION) + 1, // upper bound OEM
};
-static void mutateOperationTypeTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void mutateOperationTypeTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
for (int32_t invalidOperationType : invalidOperationTypes) {
const std::string message = "mutateOperationTypeTest: operation " +
@@ -318,8 +318,7 @@
///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX /////////////////////////
-static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device,
- const V1_0::Model& model) {
+static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const uint32_t invalidOperand = model.operands.size();
for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
@@ -335,8 +334,7 @@
///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX /////////////////////////
-static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device,
- const V1_0::Model& model) {
+static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const uint32_t invalidOperand = model.operands.size();
for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
@@ -374,7 +372,7 @@
removeValueAndDecrementGreaterValues(&model->outputIndexes, index);
}
-static void removeOperandTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void removeOperandTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
const std::string message = "removeOperandTest: operand " + std::to_string(operand);
validate(device, message, model,
@@ -391,7 +389,7 @@
hidl_vec_removeAt(&model->operations, index);
}
-static void removeOperationTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void removeOperationTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const std::string message = "removeOperationTest: operation " + std::to_string(operation);
validate(device, message, model,
@@ -401,14 +399,14 @@
///////////////////////// REMOVE OPERATION INPUT /////////////////////////
-static void removeOperationInputTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void removeOperationInputTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
- const V1_0::Operation& op = model.operations[operation];
+ const Operation& op = model.operations[operation];
// CONCATENATION has at least 2 inputs, with the last element being
// INT32. Skip this test if removing one of CONCATENATION's
// inputs still produces a valid model.
- if (op.type == V1_0::OperationType::CONCATENATION && op.inputs.size() > 2 &&
+ if (op.type == OperationType::CONCATENATION && op.inputs.size() > 2 &&
input != op.inputs.size() - 1) {
continue;
}
@@ -426,7 +424,7 @@
///////////////////////// REMOVE OPERATION OUTPUT /////////////////////////
-static void removeOperationOutputTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void removeOperationOutputTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
const std::string message = "removeOperationOutputTest: operation " +
@@ -447,7 +445,7 @@
///////////////////////// ADD OPERATION INPUT /////////////////////////
-static void addOperationInputTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void addOperationInputTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const std::string message = "addOperationInputTest: operation " + std::to_string(operation);
validate(device, message, model, [operation](Model* model) {
@@ -460,7 +458,7 @@
///////////////////////// ADD OPERATION OUTPUT /////////////////////////
-static void addOperationOutputTest(const sp<IDevice>& device, const V1_0::Model& model) {
+static void addOperationOutputTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const std::string message =
"addOperationOutputTest: operation " + std::to_string(operation);
@@ -474,7 +472,7 @@
////////////////////////// ENTRY POINT //////////////////////////////
-void ValidationTest::validateModel(const V1_0::Model& model) {
+void validateModel(const sp<IDevice>& device, const Model& model) {
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
diff --git a/neuralnetworks/1.0/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.0/vts/functional/ValidateRequest.cpp
index d8f3e65..05eefd1 100644
--- a/neuralnetworks/1.0/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.0/vts/functional/ValidateRequest.cpp
@@ -87,8 +87,7 @@
///////////////////////////// ENTRY POINT //////////////////////////////////
-void ValidationTest::validateRequest(const sp<IPreparedModel>& preparedModel,
- const Request& request) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
removeInputTest(preparedModel, request);
removeOutputTest(preparedModel, request);
}
diff --git a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
index 9ee4e37..cb22250 100644
--- a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
@@ -18,40 +18,44 @@
#include "VtsHalNeuralnetworks.h"
#include "1.0/Callbacks.h"
-#include "1.0/Utils.h"
#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include <android-base/logging.h>
+#include <hidl/ServiceManagement.h>
+#include <string>
+#include <utility>
namespace android::hardware::neuralnetworks::V1_0::vts::functional {
using implementation::PreparedModelCallback;
-static void createPreparedModel(const sp<IDevice>& device, const Model& model,
- sp<IPreparedModel>* preparedModel) {
+void createPreparedModel(const sp<IDevice>& device, const Model& model,
+ sp<IPreparedModel>* preparedModel) {
ASSERT_NE(nullptr, preparedModel);
+ *preparedModel = nullptr;
// see if service can handle model
bool fullySupportsModel = false;
- Return<void> supportedOpsLaunchStatus = device->getSupportedOperations(
+ const Return<void> supportedCall = device->getSupportedOperations(
model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
ASSERT_EQ(ErrorStatus::NONE, status);
ASSERT_NE(0ul, supported.size());
fullySupportsModel = std::all_of(supported.begin(), supported.end(),
[](bool valid) { return valid; });
});
- ASSERT_TRUE(supportedOpsLaunchStatus.isOk());
+ ASSERT_TRUE(supportedCall.isOk());
// launch prepare model
- sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus = device->prepareModel(model, preparedModelCallback);
+ const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+ const Return<ErrorStatus> prepareLaunchStatus =
+ device->prepareModel(model, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
// retrieve prepared model
preparedModelCallback->wait();
- ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+ const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
*preparedModel = preparedModelCallback->getPreparedModel();
// The getSupportedOperations call returns a list of operations that are
@@ -63,53 +67,59 @@
// can continue.
if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
ASSERT_EQ(nullptr, preparedModel->get());
- LOG(INFO) << "NN VTS: Unable to test Request validation because vendor service cannot "
- "prepare model that it does not support.";
- std::cout << "[ ] Unable to test Request validation because vendor service "
- "cannot prepare model that it does not support."
+ LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot prepare "
+ "model that it does not support.";
+ std::cout << "[ ] Early termination of test because vendor service cannot "
+ "prepare model that it does not support."
<< std::endl;
- return;
+ GTEST_SKIP();
}
ASSERT_EQ(ErrorStatus::NONE, prepareReturnStatus);
ASSERT_NE(nullptr, preparedModel->get());
}
-// A class for test environment setup
-NeuralnetworksHidlEnvironment* NeuralnetworksHidlEnvironment::getInstance() {
- // This has to return a "new" object because it is freed inside
- // ::testing::AddGlobalTestEnvironment when the gtest is being torn down
- static NeuralnetworksHidlEnvironment* instance = new NeuralnetworksHidlEnvironment();
- return instance;
-}
-
-void NeuralnetworksHidlEnvironment::registerTestServices() {
- registerTestService<IDevice>();
-}
-
-// The main test class for NEURALNETWORK HIDL HAL.
void NeuralnetworksHidlTest::SetUp() {
- ::testing::VtsHalHidlTargetTestBase::SetUp();
-
-#ifdef PRESUBMIT_NOT_VTS
- const std::string name =
- NeuralnetworksHidlEnvironment::getInstance()->getServiceName<IDevice>();
- const std::string sampleDriver = "sample-";
- if (device == nullptr && name.substr(0, sampleDriver.size()) == sampleDriver) {
- GTEST_SKIP();
- }
-#endif // PRESUBMIT_NOT_VTS
-
- ASSERT_NE(nullptr, device.get());
+ testing::TestWithParam<NeuralnetworksHidlTestParam>::SetUp();
+ ASSERT_NE(kDevice, nullptr);
}
-void NeuralnetworksHidlTest::TearDown() {
- ::testing::VtsHalHidlTargetTestBase::TearDown();
+static NamedDevice makeNamedDevice(const std::string& name) {
+ return {name, IDevice::getService(name)};
}
-void ValidationTest::validateEverything(const Model& model, const Request& request) {
- validateModel(model);
+static std::vector<NamedDevice> getNamedDevicesImpl() {
+ // Retrieves the name of all service instances that implement IDevice,
+ // including any Lazy HAL instances.
+ const std::vector<std::string> names = hardware::getAllHalInstanceNames(IDevice::descriptor);
- // create IPreparedModel
+ // Get a handle to each device and pair it with its name.
+ std::vector<NamedDevice> namedDevices;
+ namedDevices.reserve(names.size());
+ std::transform(names.begin(), names.end(), std::back_inserter(namedDevices), makeNamedDevice);
+ return namedDevices;
+}
+
+const std::vector<NamedDevice>& getNamedDevices() {
+ const static std::vector<NamedDevice> devices = getNamedDevicesImpl();
+ return devices;
+}
+
+std::string printNeuralnetworksHidlTest(
+ const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info) {
+ return gtestCompliantName(getName(info.param));
+}
+
+INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
+
+// Forward declaration from ValidateModel.cpp
+void validateModel(const sp<IDevice>& device, const Model& model);
+// Forward declaration from ValidateRequest.cpp
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request);
+
+void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
+ validateModel(device, model);
+
+ // Create IPreparedModel.
sp<IPreparedModel> preparedModel;
createPreparedModel(device, model, &preparedModel);
if (preparedModel == nullptr) return;
@@ -118,35 +128,12 @@
}
TEST_P(ValidationTest, Test) {
- const Model model = createModel(*mTestModel);
- const Request request = createRequest(*mTestModel);
- ASSERT_FALSE(mTestModel->expectFailure);
- validateEverything(model, request);
+ const Model model = createModel(kTestModel);
+ const Request request = createRequest(kTestModel);
+ ASSERT_FALSE(kTestModel.expectFailure);
+ validateEverything(kDevice, model, request);
}
INSTANTIATE_GENERATED_TEST(ValidationTest, [](const test_helper::TestModel&) { return true; });
} // namespace android::hardware::neuralnetworks::V1_0::vts::functional
-
-namespace android::hardware::neuralnetworks::V1_0 {
-
-::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
- return os << toString(errorStatus);
-}
-
-::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus) {
- return os << toString(deviceStatus);
-}
-
-} // namespace android::hardware::neuralnetworks::V1_0
-
-using android::hardware::neuralnetworks::V1_0::vts::functional::NeuralnetworksHidlEnvironment;
-
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(NeuralnetworksHidlEnvironment::getInstance());
- ::testing::InitGoogleTest(&argc, argv);
- NeuralnetworksHidlEnvironment::getInstance()->init(&argc, argv);
-
- int status = RUN_ALL_TESTS();
- return status;
-}
diff --git a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
index fa9ad3b..17f4613 100644
--- a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
@@ -17,53 +17,39 @@
#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_0_VTS_HAL_NEURALNETWORKS_H
#define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_VTS_HAL_NEURALNETWORKS_H
+#include "1.0/Utils.h"
+
#include <android/hardware/neuralnetworks/1.0/IDevice.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
-
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
-
-#include <android-base/macros.h>
#include <gtest/gtest.h>
-#include <iostream>
-#include <vector>
-#include "TestHarness.h"
+#include <vector>
namespace android::hardware::neuralnetworks::V1_0::vts::functional {
-// A class for test environment setup
-class NeuralnetworksHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- DISALLOW_COPY_AND_ASSIGN(NeuralnetworksHidlEnvironment);
- NeuralnetworksHidlEnvironment() = default;
+using NamedDevice = Named<sp<IDevice>>;
+using NeuralnetworksHidlTestParam = NamedDevice;
- public:
- static NeuralnetworksHidlEnvironment* getInstance();
- void registerTestServices() override;
-};
-
-// The main test class for NEURALNETWORKS HIDL HAL.
-class NeuralnetworksHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- DISALLOW_COPY_AND_ASSIGN(NeuralnetworksHidlTest);
-
- public:
- NeuralnetworksHidlTest() = default;
- void SetUp() override;
- void TearDown() override;
-
+class NeuralnetworksHidlTest : public testing::TestWithParam<NeuralnetworksHidlTestParam> {
protected:
- const sp<IDevice> device = ::testing::VtsHalHidlTargetTestBase::getService<IDevice>(
- NeuralnetworksHidlEnvironment::getInstance());
+ void SetUp() override;
+ const sp<IDevice> kDevice = getData(GetParam());
};
+const std::vector<NamedDevice>& getNamedDevices();
+
+std::string printNeuralnetworksHidlTest(
+ const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info);
+
+#define INSTANTIATE_DEVICE_TEST(TestSuite) \
+ INSTANTIATE_TEST_SUITE_P(PerInstance, TestSuite, testing::ValuesIn(getNamedDevices()), \
+ printNeuralnetworksHidlTest)
+
+// Create an IPreparedModel object. If the model cannot be prepared,
+// "preparedModel" will be nullptr instead.
+void createPreparedModel(const sp<IDevice>& device, const Model& model,
+ sp<IPreparedModel>* preparedModel);
+
} // namespace android::hardware::neuralnetworks::V1_0::vts::functional
-namespace android::hardware::neuralnetworks::V1_0 {
-
-// pretty-print values for error messages
-::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
-::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus);
-
-} // namespace android::hardware::neuralnetworks::V1_0
-
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_VTS_HAL_NEURALNETWORKS_H
diff --git a/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h b/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h
index 274cb58..6d4534c 100644
--- a/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h
+++ b/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h
@@ -20,13 +20,16 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
#include <algorithm>
+#include <iosfwd>
+#include <string>
+#include <utility>
#include <vector>
#include "TestHarness.h"
namespace android::hardware::neuralnetworks {
// Create HIDL Request from the TestModel struct.
-V1_0::Request createRequest(const ::test_helper::TestModel& testModel);
+V1_0::Request createRequest(const test_helper::TestModel& testModel);
// After execution, copy out output results from the output memory pool.
std::vector<::test_helper::TestBuffer> getOutputBuffers(const V1_0::Request& request);
@@ -50,6 +53,29 @@
return index;
}
+template <typename Type>
+using Named = std::pair<std::string, Type>;
+
+template <typename Type>
+const std::string& getName(const Named<Type>& namedData) {
+ return namedData.first;
+}
+
+template <typename Type>
+const Type& getData(const Named<Type>& namedData) {
+ return namedData.second;
+}
+
+std::string gtestCompliantName(std::string name);
+
} // namespace android::hardware::neuralnetworks
+namespace android::hardware::neuralnetworks::V1_0 {
+
+// pretty-print values for error messages
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
+::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus);
+
+} // namespace android::hardware::neuralnetworks::V1_0
+
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_UTILS_H
diff --git a/neuralnetworks/1.1/vts/functional/Android.bp b/neuralnetworks/1.1/vts/functional/Android.bp
index c197e6d..4e85355 100644
--- a/neuralnetworks/1.1/vts/functional/Android.bp
+++ b/neuralnetworks/1.1/vts/functional/Android.bp
@@ -14,10 +14,11 @@
// limitations under the License.
//
-cc_defaults {
- name: "VtsHalNeuralNetworksV1_1TargetTestDefaults",
+cc_test {
+ name: "VtsHalNeuralnetworksV1_1TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
+ "BasicTests.cpp",
"TestAssertions.cpp",
"ValidateModel.cpp",
"ValidateRequest.cpp",
@@ -39,35 +40,12 @@
"libneuralnetworks_utils",
"VtsHalNeuralNetworksV1_0_utils",
],
+ whole_static_libs: [
+ "neuralnetworks_generated_V1_0_example",
+ "neuralnetworks_generated_V1_1_example",
+ ],
header_libs: [
"libneuralnetworks_headers",
],
test_suites: ["general-tests"],
}
-
-cc_test {
- name: "VtsHalNeuralnetworksV1_1TargetTest",
- defaults: ["VtsHalNeuralNetworksV1_1TargetTestDefaults"],
- srcs: [
- "BasicTests.cpp",
- ],
- whole_static_libs: [
- "neuralnetworks_generated_V1_0_example",
- "neuralnetworks_generated_V1_1_example",
- ],
-}
-
-cc_test {
- name: "PresubmitHalNeuralnetworksV1_1TargetTest",
- defaults: ["VtsHalNeuralNetworksV1_1TargetTestDefaults"],
- srcs: [
- "BasicTests.cpp",
- ],
- whole_static_libs: [
- "neuralnetworks_generated_V1_0_example",
- "neuralnetworks_generated_V1_1_example",
- ],
- cflags: [
- "-DPRESUBMIT_NOT_VTS",
- ],
-}
diff --git a/neuralnetworks/1.1/vts/functional/BasicTests.cpp b/neuralnetworks/1.1/vts/functional/BasicTests.cpp
index c239c51..44836f0 100644
--- a/neuralnetworks/1.1/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.1/vts/functional/BasicTests.cpp
@@ -24,19 +24,19 @@
using V1_0::ErrorStatus;
// create device test
-TEST_F(NeuralnetworksHidlTest, CreateDevice) {}
+TEST_P(NeuralnetworksHidlTest, CreateDevice) {}
// status test
-TEST_F(NeuralnetworksHidlTest, StatusTest) {
- Return<DeviceStatus> status = device->getStatus();
+TEST_P(NeuralnetworksHidlTest, StatusTest) {
+ Return<DeviceStatus> status = kDevice->getStatus();
ASSERT_TRUE(status.isOk());
EXPECT_EQ(DeviceStatus::AVAILABLE, static_cast<DeviceStatus>(status));
}
// initialization
-TEST_F(NeuralnetworksHidlTest, GetCapabilitiesTest) {
+TEST_P(NeuralnetworksHidlTest, GetCapabilitiesTest) {
Return<void> ret =
- device->getCapabilities_1_1([](ErrorStatus status, const Capabilities& capabilities) {
+ kDevice->getCapabilities_1_1([](ErrorStatus status, const Capabilities& capabilities) {
EXPECT_EQ(ErrorStatus::NONE, status);
EXPECT_LT(0.0f, capabilities.float32Performance.execTime);
EXPECT_LT(0.0f, capabilities.float32Performance.powerUsage);
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
index 6ed5bc1..7a929d6 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
@@ -130,9 +130,15 @@
// Top level driver for models and examples generated by test_generator.py
// Test driver for those generated from ml/nn/runtime/test/spec
-void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestModel& testModel) {
+void Execute(const sp<IDevice>& device, const TestModel& testModel) {
+ const Model model = createModel(testModel);
const Request request = createRequest(testModel);
+ // Create IPreparedModel.
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
+
// Launch execution.
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
Return<ErrorStatus> executionLaunchStatus = preparedModel->execute(request, executionCallback);
@@ -150,54 +156,25 @@
checkResults(testModel, outputs);
}
+void GeneratedTestBase::SetUp() {
+ testing::TestWithParam<GeneratedTestParam>::SetUp();
+ ASSERT_NE(kDevice, nullptr);
+}
+
+std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
+ return TestModelManager::get().getTestModels(filter);
+}
+
+std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info) {
+ const auto& [namedDevice, namedModel] = info.param;
+ return gtestCompliantName(getName(namedDevice) + "_" + getName(namedModel));
+}
+
// Tag for the generated tests
-class GeneratedTest : public GeneratedTestBase {
- protected:
- void Execute(const TestModel& testModel) {
- Model model = createModel(testModel);
-
- // see if service can handle model
- bool fullySupportsModel = false;
- Return<void> supportedCall = device->getSupportedOperations_1_1(
- model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
- ASSERT_EQ(ErrorStatus::NONE, status);
- ASSERT_NE(0ul, supported.size());
- fullySupportsModel = std::all_of(supported.begin(), supported.end(),
- [](bool valid) { return valid; });
- });
- ASSERT_TRUE(supportedCall.isOk());
-
- // launch prepare model
- sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_1(
- model, ExecutionPreference::FAST_SINGLE_ANSWER, preparedModelCallback);
- ASSERT_TRUE(prepareLaunchStatus.isOk());
- ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
-
- // retrieve prepared model
- preparedModelCallback->wait();
- ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
- sp<IPreparedModel> preparedModel = preparedModelCallback->getPreparedModel();
-
- // early termination if vendor service cannot fully prepare model
- if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
- ASSERT_EQ(nullptr, preparedModel.get());
- LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
- "prepare model that it does not support.";
- std::cout << "[ ] Early termination of test because vendor service cannot "
- "prepare model that it does not support."
- << std::endl;
- GTEST_SKIP();
- }
- EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
- ASSERT_NE(nullptr, preparedModel.get());
-
- EvaluatePreparedModel(preparedModel, testModel);
- }
-};
+class GeneratedTest : public GeneratedTestBase {};
TEST_P(GeneratedTest, Test) {
- Execute(*mTestModel);
+ Execute(kDevice, kTestModel);
}
INSTANTIATE_GENERATED_TEST(GeneratedTest,
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
index 7cb9bdc..cf449ea 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
@@ -18,42 +18,38 @@
#define ANDROID_HARDWARE_NEURALNETWORKS_V1_1_GENERATED_TEST_HARNESS_H
#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include "1.0/Utils.h"
#include "TestHarness.h"
#include "VtsHalNeuralnetworks.h"
namespace android::hardware::neuralnetworks::V1_1::vts::functional {
-class GeneratedTestBase
- : public NeuralnetworksHidlTest,
- public ::testing::WithParamInterface<test_helper::TestModelManager::TestParam> {
- protected:
- void SetUp() override {
- NeuralnetworksHidlTest::SetUp();
- ASSERT_NE(mTestModel, nullptr);
- }
+using NamedModel = Named<const test_helper::TestModel*>;
+using GeneratedTestParam = std::tuple<NamedDevice, NamedModel>;
- const test_helper::TestModel* mTestModel = GetParam().second;
+class GeneratedTestBase : public testing::TestWithParam<GeneratedTestParam> {
+ protected:
+ void SetUp() override;
+ const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
+ const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
};
-#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
- INSTANTIATE_TEST_SUITE_P( \
- TestGenerated, TestSuite, \
- ::testing::ValuesIn(::test_helper::TestModelManager::get().getTestModels(filter)), \
- [](const auto& info) { return info.param.first; })
+using FilterFn = std::function<bool(const test_helper::TestModel&)>;
+std::vector<NamedModel> getNamedModels(const FilterFn& filter);
+
+std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
+
+#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
+ INSTANTIATE_TEST_SUITE_P(TestGenerated, TestSuite, \
+ testing::Combine(testing::ValuesIn(getNamedDevices()), \
+ testing::ValuesIn(getNamedModels(filter))), \
+ printGeneratedTest)
// Tag for the validation tests, instantiated in VtsHalNeuralnetworks.cpp.
// TODO: Clean up the hierarchy for ValidationTest.
-class ValidationTest : public GeneratedTestBase {
- protected:
- void validateEverything(const Model& model, const V1_0::Request& request);
+class ValidationTest : public GeneratedTestBase {};
- private:
- void validateModel(const Model& model);
- void validateRequest(const sp<V1_0::IPreparedModel>& preparedModel,
- const V1_0::Request& request);
-};
-
-Model createModel(const ::test_helper::TestModel& testModel);
+Model createModel(const test_helper::TestModel& testModel);
} // namespace android::hardware::neuralnetworks::V1_1::vts::functional
diff --git a/neuralnetworks/1.1/vts/functional/ValidateModel.cpp b/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
index e617219..0629a1e 100644
--- a/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
@@ -33,7 +33,7 @@
///////////////////////// UTILITY FUNCTIONS /////////////////////////
static void validateGetSupportedOperations(const sp<IDevice>& device, const std::string& message,
- const V1_1::Model& model) {
+ const Model& model) {
SCOPED_TRACE(message + " [getSupportedOperations_1_1]");
Return<void> ret = device->getSupportedOperations_1_1(
@@ -44,7 +44,7 @@
}
static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
- const V1_1::Model& model, ExecutionPreference preference) {
+ const Model& model, ExecutionPreference preference) {
SCOPED_TRACE(message + " [prepareModel_1_1]");
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
@@ -70,7 +70,7 @@
// mutation to it to invalidate the model, then pass it to interface calls that
// use the model. Note that the model here is passed by value, and any mutation
// to the model does not leave this function.
-static void validate(const sp<IDevice>& device, const std::string& message, V1_1::Model model,
+static void validate(const sp<IDevice>& device, const std::string& message, Model model,
const std::function<void(Model*)>& mutation,
ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER) {
mutation(&model);
@@ -109,7 +109,7 @@
static_cast<int32_t>(OperandType::TENSOR_OEM_BYTE) + 1, // upper bound OEM
};
-static void mutateOperandTypeTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void mutateOperandTypeTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
for (int32_t invalidOperandType : invalidOperandTypes) {
const std::string message = "mutateOperandTypeTest: operand " +
@@ -139,7 +139,7 @@
}
}
-static void mutateOperandRankTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void mutateOperandRankTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
const uint32_t invalidRank = getInvalidRank(model.operands[operand].type);
const std::string message = "mutateOperandRankTest: operand " + std::to_string(operand) +
@@ -168,7 +168,7 @@
}
}
-static void mutateOperandScaleTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void mutateOperandScaleTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
const float invalidScale = getInvalidScale(model.operands[operand].type);
const std::string message = "mutateOperandScaleTest: operand " + std::to_string(operand) +
@@ -196,7 +196,7 @@
}
}
-static void mutateOperandZeroPointTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void mutateOperandZeroPointTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
const std::vector<int32_t> invalidZeroPoints =
getInvalidZeroPoints(model.operands[operand].type);
@@ -253,7 +253,7 @@
*operand = newOperand;
}
-static bool mutateOperationOperandTypeSkip(size_t operand, const V1_1::Model& model) {
+static bool mutateOperationOperandTypeSkip(size_t operand, const Model& model) {
// LSH_PROJECTION's second argument is allowed to have any type. This is the
// only operation that currently has a type that can be anything independent
// from any other type. Changing the operand type to any other type will
@@ -267,7 +267,7 @@
return false;
}
-static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void mutateOperationOperandTypeTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
if (mutateOperationOperandTypeSkip(operand, model)) {
continue;
@@ -298,7 +298,7 @@
static_cast<int32_t>(OperationType::OEM_OPERATION) + 1, // upper bound OEM
};
-static void mutateOperationTypeTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void mutateOperationTypeTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
for (int32_t invalidOperationType : invalidOperationTypes) {
const std::string message = "mutateOperationTypeTest: operation " +
@@ -314,8 +314,7 @@
///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX /////////////////////////
-static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device,
- const V1_1::Model& model) {
+static void mutateOperationInputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const uint32_t invalidOperand = model.operands.size();
for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
@@ -331,8 +330,7 @@
///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX /////////////////////////
-static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device,
- const V1_1::Model& model) {
+static void mutateOperationOutputOperandIndexTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const uint32_t invalidOperand = model.operands.size();
for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
@@ -370,7 +368,7 @@
removeValueAndDecrementGreaterValues(&model->outputIndexes, index);
}
-static void removeOperandTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void removeOperandTest(const sp<IDevice>& device, const Model& model) {
for (size_t operand = 0; operand < model.operands.size(); ++operand) {
const std::string message = "removeOperandTest: operand " + std::to_string(operand);
validate(device, message, model,
@@ -387,7 +385,7 @@
hidl_vec_removeAt(&model->operations, index);
}
-static void removeOperationTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void removeOperationTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const std::string message = "removeOperationTest: operation " + std::to_string(operation);
validate(device, message, model,
@@ -397,14 +395,14 @@
///////////////////////// REMOVE OPERATION INPUT /////////////////////////
-static void removeOperationInputTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void removeOperationInputTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
for (size_t input = 0; input < model.operations[operation].inputs.size(); ++input) {
- const V1_1::Operation& op = model.operations[operation];
+ const Operation& op = model.operations[operation];
// CONCATENATION has at least 2 inputs, with the last element being
// INT32. Skip this test if removing one of CONCATENATION's
// inputs still produces a valid model.
- if (op.type == V1_1::OperationType::CONCATENATION && op.inputs.size() > 2 &&
+ if (op.type == OperationType::CONCATENATION && op.inputs.size() > 2 &&
input != op.inputs.size() - 1) {
continue;
}
@@ -422,7 +420,7 @@
///////////////////////// REMOVE OPERATION OUTPUT /////////////////////////
-static void removeOperationOutputTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void removeOperationOutputTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
for (size_t output = 0; output < model.operations[operation].outputs.size(); ++output) {
const std::string message = "removeOperationOutputTest: operation " +
@@ -443,7 +441,7 @@
///////////////////////// ADD OPERATION INPUT /////////////////////////
-static void addOperationInputTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void addOperationInputTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const std::string message = "addOperationInputTest: operation " + std::to_string(operation);
validate(device, message, model, [operation](Model* model) {
@@ -456,7 +454,7 @@
///////////////////////// ADD OPERATION OUTPUT /////////////////////////
-static void addOperationOutputTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void addOperationOutputTest(const sp<IDevice>& device, const Model& model) {
for (size_t operation = 0; operation < model.operations.size(); ++operation) {
const std::string message =
"addOperationOutputTest: operation " + std::to_string(operation);
@@ -475,7 +473,7 @@
static_cast<int32_t>(ExecutionPreference::SUSTAINED_SPEED) + 1, // upper bound
};
-static void mutateExecutionPreferenceTest(const sp<IDevice>& device, const V1_1::Model& model) {
+static void mutateExecutionPreferenceTest(const sp<IDevice>& device, const Model& model) {
for (int32_t preference : invalidExecutionPreferences) {
const std::string message =
"mutateExecutionPreferenceTest: preference " + std::to_string(preference);
@@ -487,7 +485,7 @@
////////////////////////// ENTRY POINT //////////////////////////////
-void ValidationTest::validateModel(const V1_1::Model& model) {
+void validateModel(const sp<IDevice>& device, const Model& model) {
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
diff --git a/neuralnetworks/1.1/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.1/vts/functional/ValidateRequest.cpp
index a4e4ade..9684eb2 100644
--- a/neuralnetworks/1.1/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.1/vts/functional/ValidateRequest.cpp
@@ -71,8 +71,7 @@
///////////////////////////// ENTRY POINT //////////////////////////////////
-void ValidationTest::validateRequest(const sp<IPreparedModel>& preparedModel,
- const Request& request) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
removeInputTest(preparedModel, request);
removeOutputTest(preparedModel, request);
}
diff --git a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
index 2c1a839..d56d40b 100644
--- a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
@@ -17,13 +17,15 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
#include "VtsHalNeuralnetworks.h"
+#include <android-base/logging.h>
+#include <hidl/ServiceManagement.h>
+#include <string>
+#include <utility>
#include "1.0/Callbacks.h"
#include "1.0/Utils.h"
#include "GeneratedTestHarness.h"
#include "TestHarness.h"
-#include <android-base/logging.h>
-
namespace android::hardware::neuralnetworks::V1_1::vts::functional {
using V1_0::ErrorStatus;
@@ -31,31 +33,32 @@
using V1_0::Request;
using V1_0::implementation::PreparedModelCallback;
-static void createPreparedModel(const sp<IDevice>& device, const Model& model,
- sp<IPreparedModel>* preparedModel) {
+void createPreparedModel(const sp<IDevice>& device, const Model& model,
+ sp<IPreparedModel>* preparedModel) {
ASSERT_NE(nullptr, preparedModel);
+ *preparedModel = nullptr;
// see if service can handle model
bool fullySupportsModel = false;
- Return<void> supportedOpsLaunchStatus = device->getSupportedOperations_1_1(
+ const Return<void> supportedCall = device->getSupportedOperations_1_1(
model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
ASSERT_EQ(ErrorStatus::NONE, status);
ASSERT_NE(0ul, supported.size());
fullySupportsModel = std::all_of(supported.begin(), supported.end(),
[](bool valid) { return valid; });
});
- ASSERT_TRUE(supportedOpsLaunchStatus.isOk());
+ ASSERT_TRUE(supportedCall.isOk());
// launch prepare model
- sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_1(
+ const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+ const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_1(
model, ExecutionPreference::FAST_SINGLE_ANSWER, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
// retrieve prepared model
preparedModelCallback->wait();
- ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+ const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
*preparedModel = preparedModelCallback->getPreparedModel();
// The getSupportedOperations_1_1 call returns a list of operations that are
@@ -67,92 +70,73 @@
// can continue.
if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
ASSERT_EQ(nullptr, preparedModel->get());
- LOG(INFO) << "NN VTS: Unable to test Request validation because vendor service cannot "
- "prepare model that it does not support.";
- std::cout << "[ ] Unable to test Request validation because vendor service "
- "cannot prepare model that it does not support."
+ LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot prepare "
+ "model that it does not support.";
+ std::cout << "[ ] Early termination of test because vendor service cannot "
+ "prepare model that it does not support."
<< std::endl;
- return;
+ GTEST_SKIP();
}
ASSERT_EQ(ErrorStatus::NONE, prepareReturnStatus);
ASSERT_NE(nullptr, preparedModel->get());
}
-// A class for test environment setup
-NeuralnetworksHidlEnvironment* NeuralnetworksHidlEnvironment::getInstance() {
- // This has to return a "new" object because it is freed inside
- // ::testing::AddGlobalTestEnvironment when the gtest is being torn down
- static NeuralnetworksHidlEnvironment* instance = new NeuralnetworksHidlEnvironment();
- return instance;
-}
-
-void NeuralnetworksHidlEnvironment::registerTestServices() {
- registerTestService<IDevice>();
-}
-
-// The main test class for NEURALNETWORK HIDL HAL.
void NeuralnetworksHidlTest::SetUp() {
- ::testing::VtsHalHidlTargetTestBase::SetUp();
-
-#ifdef PRESUBMIT_NOT_VTS
- const std::string name =
- NeuralnetworksHidlEnvironment::getInstance()->getServiceName<IDevice>();
- const std::string sampleDriver = "sample-";
- if (device == nullptr && name.substr(0, sampleDriver.size()) == sampleDriver) {
- GTEST_SKIP();
- }
-#endif // PRESUBMIT_NOT_VTS
-
- ASSERT_NE(nullptr, device.get());
+ testing::TestWithParam<NeuralnetworksHidlTestParam>::SetUp();
+ ASSERT_NE(kDevice, nullptr);
}
-void NeuralnetworksHidlTest::TearDown() {
- ::testing::VtsHalHidlTargetTestBase::TearDown();
+static NamedDevice makeNamedDevice(const std::string& name) {
+ return {name, IDevice::getService(name)};
}
-void ValidationTest::validateEverything(const Model& model, const Request& request) {
- validateModel(model);
+static std::vector<NamedDevice> getNamedDevicesImpl() {
+ // Retrieves the name of all service instances that implement IDevice,
+ // including any Lazy HAL instances.
+ const std::vector<std::string> names = hardware::getAllHalInstanceNames(IDevice::descriptor);
- // create IPreparedModel
+ // Get a handle to each device and pair it with its name.
+ std::vector<NamedDevice> namedDevices;
+ namedDevices.reserve(names.size());
+ std::transform(names.begin(), names.end(), std::back_inserter(namedDevices), makeNamedDevice);
+ return namedDevices;
+}
+
+const std::vector<NamedDevice>& getNamedDevices() {
+ const static std::vector<NamedDevice> devices = getNamedDevicesImpl();
+ return devices;
+}
+
+std::string printNeuralnetworksHidlTest(
+ const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info) {
+ return gtestCompliantName(getName(info.param));
+}
+
+INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
+
+// Forward declaration from ValidateModel.cpp
+void validateModel(const sp<IDevice>& device, const Model& model);
+// Forward declaration from ValidateRequest.cpp
+void validateRequest(const sp<V1_0::IPreparedModel>& preparedModel, const V1_0::Request& request);
+
+void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
+ validateModel(device, model);
+
+ // Create IPreparedModel.
sp<IPreparedModel> preparedModel;
- ASSERT_NO_FATAL_FAILURE(createPreparedModel(device, model, &preparedModel));
- if (preparedModel == nullptr) {
- return;
- }
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
validateRequest(preparedModel, request);
}
TEST_P(ValidationTest, Test) {
- const Model model = createModel(*mTestModel);
- const Request request = createRequest(*mTestModel);
- ASSERT_FALSE(mTestModel->expectFailure);
- validateEverything(model, request);
+ const Model model = createModel(kTestModel);
+ const Request request = createRequest(kTestModel);
+ ASSERT_FALSE(kTestModel.expectFailure);
+ validateEverything(kDevice, model, request);
}
INSTANTIATE_GENERATED_TEST(ValidationTest, [](const test_helper::TestModel&) { return true; });
} // namespace android::hardware::neuralnetworks::V1_1::vts::functional
-
-namespace android::hardware::neuralnetworks::V1_0 {
-
-::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
- return os << toString(errorStatus);
-}
-
-::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus) {
- return os << toString(deviceStatus);
-}
-
-} // namespace android::hardware::neuralnetworks::V1_0
-
-using android::hardware::neuralnetworks::V1_1::vts::functional::NeuralnetworksHidlEnvironment;
-
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(NeuralnetworksHidlEnvironment::getInstance());
- ::testing::InitGoogleTest(&argc, argv);
- NeuralnetworksHidlEnvironment::getInstance()->init(&argc, argv);
-
- int status = RUN_ALL_TESTS();
- return status;
-}
diff --git a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
index 3d6f2ea..e879d84 100644
--- a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
@@ -17,54 +17,38 @@
#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_1_VTS_HAL_NEURALNETWORKS_H
#define ANDROID_HARDWARE_NEURALNETWORKS_V1_1_VTS_HAL_NEURALNETWORKS_H
-#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
#include <android/hardware/neuralnetworks/1.1/IDevice.h>
#include <android/hardware/neuralnetworks/1.1/types.h>
-
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
-
-#include <android-base/macros.h>
#include <gtest/gtest.h>
-#include <iostream>
#include <vector>
-
-#include "TestHarness.h"
+#include "1.0/Utils.h"
namespace android::hardware::neuralnetworks::V1_1::vts::functional {
-// A class for test environment setup
-class NeuralnetworksHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- DISALLOW_COPY_AND_ASSIGN(NeuralnetworksHidlEnvironment);
- NeuralnetworksHidlEnvironment() = default;
+using NamedDevice = Named<sp<IDevice>>;
+using NeuralnetworksHidlTestParam = NamedDevice;
- public:
- static NeuralnetworksHidlEnvironment* getInstance();
- void registerTestServices() override;
-};
-
-// The main test class for NEURALNETWORKS HIDL HAL.
-class NeuralnetworksHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- DISALLOW_COPY_AND_ASSIGN(NeuralnetworksHidlTest);
-
- public:
- NeuralnetworksHidlTest() = default;
- void SetUp() override;
- void TearDown() override;
-
+class NeuralnetworksHidlTest : public testing::TestWithParam<NeuralnetworksHidlTestParam> {
protected:
- const sp<IDevice> device = ::testing::VtsHalHidlTargetTestBase::getService<IDevice>(
- NeuralnetworksHidlEnvironment::getInstance());
+ void SetUp() override;
+ const sp<IDevice> kDevice = getData(GetParam());
};
+const std::vector<NamedDevice>& getNamedDevices();
+
+std::string printNeuralnetworksHidlTest(
+ const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info);
+
+#define INSTANTIATE_DEVICE_TEST(TestSuite) \
+ INSTANTIATE_TEST_SUITE_P(PerInstance, TestSuite, testing::ValuesIn(getNamedDevices()), \
+ printNeuralnetworksHidlTest)
+
+// Create an IPreparedModel object. If the model cannot be prepared,
+// "preparedModel" will be nullptr instead.
+void createPreparedModel(const sp<IDevice>& device, const Model& model,
+ sp<V1_0::IPreparedModel>* preparedModel);
+
} // namespace android::hardware::neuralnetworks::V1_1::vts::functional
-namespace android::hardware::neuralnetworks::V1_0 {
-
-// pretty-print values for error messages
-::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
-::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus);
-
-} // namespace android::hardware::neuralnetworks::V1_0
-
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_1_VTS_HAL_NEURALNETWORKS_H
diff --git a/neuralnetworks/1.2/IPreparedModel.hal b/neuralnetworks/1.2/IPreparedModel.hal
index ba16334..1445f18 100644
--- a/neuralnetworks/1.2/IPreparedModel.hal
+++ b/neuralnetworks/1.2/IPreparedModel.hal
@@ -54,11 +54,14 @@
* must not change the content of any of the data objects corresponding to
* 'request' inputs.
*
- * If the prepared model was prepared from a model wherein all
- * tensor operands have fully specified dimensions, and the inputs
- * to the function are valid, then the execution should launch
- * and complete successfully (ErrorStatus::NONE). There must be
- * no failure unless the device itself is in a bad state.
+ * If the prepared model was prepared from a model wherein all tensor
+ * operands have fully specified dimensions, and the inputs to the function
+ * are valid, then:
+ * - the execution should launch successfully (ErrorStatus::NONE): There
+ * must be no failure unless the device itself is in a bad state.
+ * - if at execution time every operation's input operands have legal
+ * values, the execution should complete successfully (ErrorStatus::NONE):
+ * There must be no failure unless the device itself is in a bad state.
*
* Any number of calls to the execute, execute_1_2, and executeSynchronously
* functions, in any combination, may be made concurrently, even on the same
@@ -105,8 +108,9 @@
*
* If the prepared model was prepared from a model wherein all tensor
* operands have fully specified dimensions, and the inputs to the function
- * are valid, then the execution should complete successfully
- * (ErrorStatus::NONE). There must be no failure unless the device itself is
+ * are valid, and at execution time every operation's input operands have
+ * legal values, then the execution should complete successfully
+ * (ErrorStatus::NONE): There must be no failure unless the device itself is
* in a bad state.
*
* Any number of calls to the execute, execute_1_2, and executeSynchronously
@@ -145,23 +149,52 @@
* Configure a Burst object used to execute multiple inferences on a
* prepared model in rapid succession.
*
+ * If the prepared model was prepared from a model wherein all tensor
+ * operands have fully specified dimensions, and a valid serialized Request
+ * is sent to the Burst for execution, and at execution time every
+ * operation's input operands have legal values, then the execution should
+ * complete successfully (ErrorStatus::NONE): There must be no failure
+ * unless the device itself is in a bad state.
+ *
* @param callback A callback object used to retrieve memory resources
- * corresponding to a unique identifiers ("slots").
- * @param requestChannel Used by the client to send a serialized Request to
- * the Burst for execution. The client must not change
- * the content of any data object referenced by the
- * Request (described by the {@link @1.0::DataLocation}
- * of an {@link OperandInformation}) until a result
- * has been received from resultChannel. Execution
- * must not change the content of any of the data
- * objects corresponding to Request inputs. requestChannel
+ * corresponding to unique identifiers ("slots").
+ * @param requestChannel FMQ used by the client to send a serialized Request
+ * to the Burst for execution. The client must not
+ * change the content of any data object referenced by
+ * the Request (described by the
+ * {@link @1.0::DataLocation} of an
+ * {@link OperandInformation}) until a result has been
+ * received from resultChannel. Execution must not
+ * change the content of any of the data objects
+ * corresponding to Request inputs. requestChannel
* must not be used to pass a second Request object
- * until a result has been received from resultChannel.
- * @param resultChannel Used by the service to return the results of an
- * execution to the client: the status of the execution
- * and OutputShape of all output tensors. resultChannel
- * must be used to return the results if a Request was
- * sent through the requestChannel.
+ * until a result has been received from
+ * resultChannel. The client must send the request
+ * messages to the consumer atomically by using
+ * MessageQueue::writeBlocking if the queue is
+ * blocking, or by using MessageQueue::write if the
+ * queue is non-blocking. When the service receives a
+ * packet, it must dequeue the entire packet from the
+ * requestChannel. The client must not send a request
+ * packet that exceeds the length of the FMQ.
+ * @param resultChannel FMQ used by the service to return the results of an
+ * execution to the client: the status of the
+ * execution, OutputShape of all output tensors, and
+ * timing information. resultChannel must be used to
+ * return the results if a Request was sent through the
+ * requestChannel. The service must send the result
+ * messages to the consumer atomically by using
+ * MessageQueue::writeBlocking if the queue is
+ * blocking, or by using MessageQueue::write if the
+ * queue is non-blocking. When the client receives a
+ * packet, it must dequeue the entire packet from the
+ * resultChannel. If the packet's length exceeds the
+ * size of the FMQ, the service must not send this
+ * result packet; instead, the service must send a
+ * packet consisting of the error code
+ * ErrorStatus::GENERAL_FAILURE, no information for the
+ * outputShapes, and an indication that timing
+ * information is unavailable.
* @return status Error status of configuring the execution burst, must be:
* - NONE if the burst is successfully configured
* - DEVICE_UNAVAILABLE if driver is offline or busy
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index 40ca809..3ba8879 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -14,16 +14,19 @@
// limitations under the License.
//
-cc_defaults {
- name: "VtsHalNeuralNetworksV1_2TargetTestDefaults",
+cc_test {
+ name: "VtsHalNeuralnetworksV1_2TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
+ "BasicTests.cpp",
+ "Callbacks.cpp",
+ "CompilationCachingTests.cpp",
+ "GeneratedTestHarness.cpp",
"TestAssertions.cpp",
"ValidateModel.cpp",
"ValidateRequest.cpp",
+ "ValidateBurst.cpp",
"VtsHalNeuralnetworks.cpp",
- "Callbacks.cpp",
- "GeneratedTestHarness.cpp",
],
local_include_dirs: ["include"],
shared_libs: [
@@ -42,41 +45,13 @@
"libneuralnetworks_utils",
"VtsHalNeuralNetworksV1_0_utils",
],
+ whole_static_libs: [
+ "neuralnetworks_generated_V1_0_example",
+ "neuralnetworks_generated_V1_1_example",
+ "neuralnetworks_generated_V1_2_example",
+ ],
header_libs: [
"libneuralnetworks_headers",
],
test_suites: ["general-tests"],
}
-
-cc_test {
- name: "VtsHalNeuralnetworksV1_2TargetTest",
- defaults: ["VtsHalNeuralNetworksV1_2TargetTestDefaults"],
- srcs: [
- "BasicTests.cpp",
- "CompilationCachingTests.cpp",
- "ValidateBurst.cpp",
- ],
- whole_static_libs: [
- "neuralnetworks_generated_V1_0_example",
- "neuralnetworks_generated_V1_1_example",
- "neuralnetworks_generated_V1_2_example",
- ],
-}
-
-cc_test {
- name: "PresubmitHalNeuralnetworksV1_2TargetTest",
- defaults: ["VtsHalNeuralNetworksV1_2TargetTestDefaults"],
- srcs: [
- "BasicTests.cpp",
- "CompilationCachingTests.cpp",
- "ValidateBurst.cpp",
- ],
- whole_static_libs: [
- "neuralnetworks_generated_V1_0_example",
- "neuralnetworks_generated_V1_1_example",
- "neuralnetworks_generated_V1_2_example",
- ],
- cflags: [
- "-DPRESUBMIT_NOT_VTS",
- ],
-}
diff --git a/neuralnetworks/1.2/vts/functional/BasicTests.cpp b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
index 86849d5..8e82c53 100644
--- a/neuralnetworks/1.2/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
@@ -25,20 +25,20 @@
using V1_0::PerformanceInfo;
// create device test
-TEST_F(NeuralnetworksHidlTest, CreateDevice) {}
+TEST_P(NeuralnetworksHidlTest, CreateDevice) {}
// status test
-TEST_F(NeuralnetworksHidlTest, StatusTest) {
- Return<DeviceStatus> status = device->getStatus();
+TEST_P(NeuralnetworksHidlTest, StatusTest) {
+ Return<DeviceStatus> status = kDevice->getStatus();
ASSERT_TRUE(status.isOk());
EXPECT_EQ(DeviceStatus::AVAILABLE, static_cast<DeviceStatus>(status));
}
// initialization
-TEST_F(NeuralnetworksHidlTest, GetCapabilitiesTest) {
+TEST_P(NeuralnetworksHidlTest, GetCapabilitiesTest) {
using OperandPerformance = Capabilities::OperandPerformance;
- Return<void> ret = device->getCapabilities_1_2([](ErrorStatus status,
- const Capabilities& capabilities) {
+ Return<void> ret = kDevice->getCapabilities_1_2([](ErrorStatus status,
+ const Capabilities& capabilities) {
EXPECT_EQ(ErrorStatus::NONE, status);
auto isPositive = [](const PerformanceInfo& perf) {
@@ -60,17 +60,18 @@
}
// device version test
-TEST_F(NeuralnetworksHidlTest, GetDeviceVersionStringTest) {
- Return<void> ret = device->getVersionString([](ErrorStatus status, const hidl_string& version) {
- EXPECT_EQ(ErrorStatus::NONE, status);
- EXPECT_LT(0, version.size());
- });
+TEST_P(NeuralnetworksHidlTest, GetDeviceVersionStringTest) {
+ Return<void> ret =
+ kDevice->getVersionString([](ErrorStatus status, const hidl_string& version) {
+ EXPECT_EQ(ErrorStatus::NONE, status);
+ EXPECT_LT(0, version.size());
+ });
EXPECT_TRUE(ret.isOk());
}
// device type test
-TEST_F(NeuralnetworksHidlTest, GetDeviceTypeTest) {
- Return<void> ret = device->getType([](ErrorStatus status, DeviceType type) {
+TEST_P(NeuralnetworksHidlTest, GetDeviceTypeTest) {
+ Return<void> ret = kDevice->getType([](ErrorStatus status, DeviceType type) {
EXPECT_EQ(ErrorStatus::NONE, status);
EXPECT_TRUE(type == DeviceType::OTHER || type == DeviceType::CPU ||
type == DeviceType::GPU || type == DeviceType::ACCELERATOR);
@@ -79,8 +80,8 @@
}
// device supported extensions test
-TEST_F(NeuralnetworksHidlTest, GetDeviceSupportedExtensionsTest) {
- Return<void> ret = device->getSupportedExtensions(
+TEST_P(NeuralnetworksHidlTest, GetDeviceSupportedExtensionsTest) {
+ Return<void> ret = kDevice->getSupportedExtensions(
[](ErrorStatus status, const hidl_vec<Extension>& extensions) {
EXPECT_EQ(ErrorStatus::NONE, status);
for (auto& extension : extensions) {
@@ -100,8 +101,8 @@
}
// getNumberOfCacheFilesNeeded test
-TEST_F(NeuralnetworksHidlTest, getNumberOfCacheFilesNeeded) {
- Return<void> ret = device->getNumberOfCacheFilesNeeded(
+TEST_P(NeuralnetworksHidlTest, getNumberOfCacheFilesNeeded) {
+ Return<void> ret = kDevice->getNumberOfCacheFilesNeeded(
[](ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
EXPECT_EQ(ErrorStatus::NONE, status);
EXPECT_LE(numModelCache,
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 90872d4..2130a76 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
#include <android-base/logging.h>
+#include <fcntl.h>
#include <ftw.h>
#include <gtest/gtest.h>
#include <hidlmemory/mapping.h>
@@ -37,11 +38,11 @@
// Forward declaration of the mobilenet generated test models in
// frameworks/ml/nn/runtime/test/generated/.
namespace generated_tests::mobilenet_224_gender_basic_fixed {
-const ::test_helper::TestModel& get_test_model();
+const test_helper::TestModel& get_test_model();
} // namespace generated_tests::mobilenet_224_gender_basic_fixed
namespace generated_tests::mobilenet_quantized {
-const ::test_helper::TestModel& get_test_model();
+const test_helper::TestModel& get_test_model();
} // namespace generated_tests::mobilenet_quantized
namespace android::hardware::neuralnetworks::V1_2::vts::functional {
@@ -53,13 +54,13 @@
namespace float32_model {
-constexpr auto get_test_model = ::generated_tests::mobilenet_224_gender_basic_fixed::get_test_model;
+constexpr auto get_test_model = generated_tests::mobilenet_224_gender_basic_fixed::get_test_model;
} // namespace float32_model
namespace quant8_model {
-constexpr auto get_test_model = ::generated_tests::mobilenet_quantized::get_test_model;
+constexpr auto get_test_model = generated_tests::mobilenet_quantized::get_test_model;
} // namespace quant8_model
@@ -217,13 +218,14 @@
} // namespace
// Tag for the compilation caching tests.
-class CompilationCachingTestBase : public NeuralnetworksHidlTest {
+class CompilationCachingTestBase : public testing::Test {
protected:
- CompilationCachingTestBase(OperandType type) : kOperandType(type) {}
+ CompilationCachingTestBase(sp<IDevice> device, OperandType type)
+ : kDevice(std::move(device)), kOperandType(type) {}
void SetUp() override {
- NeuralnetworksHidlTest::SetUp();
- ASSERT_NE(device.get(), nullptr);
+ testing::Test::SetUp();
+ ASSERT_NE(kDevice.get(), nullptr);
// Create cache directory. The cache directory and a temporary cache file is always created
// to test the behavior of prepareModelFromCache, even when caching is not supported.
@@ -233,7 +235,7 @@
mCacheDir = cacheDir;
mCacheDir.push_back('/');
- Return<void> ret = device->getNumberOfCacheFilesNeeded(
+ Return<void> ret = kDevice->getNumberOfCacheFilesNeeded(
[this](ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
EXPECT_EQ(ErrorStatus::NONE, status);
mNumModelCache = numModelCache;
@@ -267,14 +269,14 @@
void TearDown() override {
// If the test passes, remove the tmp directory. Otherwise, keep it for debugging purposes.
- if (!::testing::Test::HasFailure()) {
+ if (!testing::Test::HasFailure()) {
// Recursively remove the cache directory specified by mCacheDir.
auto callback = [](const char* entry, const struct stat*, int, struct FTW*) {
return remove(entry);
};
nftw(mCacheDir.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
}
- NeuralnetworksHidlTest::TearDown();
+ testing::Test::TearDown();
}
// Model and examples creators. According to kOperandType, the following methods will return
@@ -300,7 +302,7 @@
// See if the service can handle the model.
bool isModelFullySupported(const Model& model) {
bool fullySupportsModel = false;
- Return<void> supportedCall = device->getSupportedOperations_1_2(
+ Return<void> supportedCall = kDevice->getSupportedOperations_1_2(
model,
[&fullySupportsModel, &model](ErrorStatus status, const hidl_vec<bool>& supported) {
ASSERT_EQ(ErrorStatus::NONE, status);
@@ -321,8 +323,8 @@
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
Return<ErrorStatus> prepareLaunchStatus =
- device->prepareModel_1_2(model, ExecutionPreference::FAST_SINGLE_ANSWER, modelCache,
- dataCache, cacheToken, preparedModelCallback);
+ kDevice->prepareModel_1_2(model, ExecutionPreference::FAST_SINGLE_ANSWER,
+ modelCache, dataCache, cacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(static_cast<ErrorStatus>(prepareLaunchStatus), ErrorStatus::NONE);
@@ -365,7 +367,7 @@
// Launch prepare model from cache.
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
- Return<ErrorStatus> prepareLaunchStatus = device->prepareModelFromCache(
+ Return<ErrorStatus> prepareLaunchStatus = kDevice->prepareModelFromCache(
modelCache, dataCache, cacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
if (static_cast<ErrorStatus>(prepareLaunchStatus) != ErrorStatus::NONE) {
@@ -398,16 +400,21 @@
uint32_t mNumDataCache;
uint32_t mIsCachingSupported;
+ const sp<IDevice> kDevice;
// The primary data type of the testModel.
const OperandType kOperandType;
};
+using CompilationCachingTestParam = std::tuple<NamedDevice, OperandType>;
+
// A parameterized fixture of CompilationCachingTestBase. Every test will run twice, with the first
// pass running with float32 models and the second pass running with quant8 models.
class CompilationCachingTest : public CompilationCachingTestBase,
- public ::testing::WithParamInterface<OperandType> {
+ public testing::WithParamInterface<CompilationCachingTestParam> {
protected:
- CompilationCachingTest() : CompilationCachingTestBase(GetParam()) {}
+ CompilationCachingTest()
+ : CompilationCachingTestBase(getData(std::get<NamedDevice>(GetParam())),
+ std::get<OperandType>(GetParam())) {}
};
TEST_P(CompilationCachingTest, CacheSavingAndRetrieval) {
@@ -1192,16 +1199,30 @@
}
}
+static const auto kNamedDeviceChoices = testing::ValuesIn(getNamedDevices());
static const auto kOperandTypeChoices =
- ::testing::Values(OperandType::TENSOR_FLOAT32, OperandType::TENSOR_QUANT8_ASYMM);
+ testing::Values(OperandType::TENSOR_FLOAT32, OperandType::TENSOR_QUANT8_ASYMM);
-INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingTest, kOperandTypeChoices);
+std::string printCompilationCachingTest(
+ const testing::TestParamInfo<CompilationCachingTestParam>& info) {
+ const auto& [namedDevice, operandType] = info.param;
+ const std::string type = (operandType == OperandType::TENSOR_FLOAT32 ? "float32" : "quant8");
+ return gtestCompliantName(getName(namedDevice) + "_" + type);
+}
+
+INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingTest,
+ testing::Combine(kNamedDeviceChoices, kOperandTypeChoices),
+ printCompilationCachingTest);
+
+using CompilationCachingSecurityTestParam = std::tuple<NamedDevice, OperandType, uint32_t>;
class CompilationCachingSecurityTest
: public CompilationCachingTestBase,
- public ::testing::WithParamInterface<std::tuple<OperandType, uint32_t>> {
+ public testing::WithParamInterface<CompilationCachingSecurityTestParam> {
protected:
- CompilationCachingSecurityTest() : CompilationCachingTestBase(std::get<0>(GetParam())) {}
+ CompilationCachingSecurityTest()
+ : CompilationCachingTestBase(getData(std::get<NamedDevice>(GetParam())),
+ std::get<OperandType>(GetParam())) {}
void SetUp() {
CompilationCachingTestBase::SetUp();
@@ -1291,7 +1312,7 @@
}
}
- const uint32_t kSeed = std::get<1>(GetParam());
+ const uint32_t kSeed = std::get<uint32_t>(GetParam());
std::mt19937 generator;
};
@@ -1338,7 +1359,16 @@
});
}
+std::string printCompilationCachingSecurityTest(
+ const testing::TestParamInfo<CompilationCachingSecurityTestParam>& info) {
+ const auto& [namedDevice, operandType, seed] = info.param;
+ const std::string type = (operandType == OperandType::TENSOR_FLOAT32 ? "float32" : "quant8");
+ return gtestCompliantName(getName(namedDevice) + "_" + type + "_" + std::to_string(seed));
+}
+
INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingSecurityTest,
- ::testing::Combine(kOperandTypeChoices, ::testing::Range(0U, 10U)));
+ testing::Combine(kNamedDeviceChoices, kOperandTypeChoices,
+ testing::Range(0U, 10U)),
+ printCompilationCachingSecurityTest);
} // namespace android::hardware::neuralnetworks::V1_2::vts::functional
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
index b8ca080..2beec98 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
@@ -190,7 +190,7 @@
}
static std::shared_ptr<::android::nn::ExecutionBurstController> CreateBurst(
const sp<IPreparedModel>& preparedModel) {
- return ::android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
+ return android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
}
enum class Executor { ASYNC, SYNC, BURST };
@@ -358,74 +358,45 @@
}
}
-void PrepareModel(const sp<IDevice>& device, const Model& model,
- sp<IPreparedModel>* preparedModel) {
- // see if service can handle model
- bool fullySupportsModel = false;
- Return<void> supportedCall = device->getSupportedOperations_1_2(
- model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
- ASSERT_EQ(ErrorStatus::NONE, status);
- ASSERT_NE(0ul, supported.size());
- fullySupportsModel = std::all_of(supported.begin(), supported.end(),
- [](bool valid) { return valid; });
- });
- ASSERT_TRUE(supportedCall.isOk());
-
- // launch prepare model
- sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_2(
- model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
- hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
- ASSERT_TRUE(prepareLaunchStatus.isOk());
- ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
-
- // retrieve prepared model
- preparedModelCallback->wait();
- ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
- sp<V1_0::IPreparedModel> preparedModelV1_0 = preparedModelCallback->getPreparedModel();
- *preparedModel = IPreparedModel::castFrom(preparedModelV1_0).withDefault(nullptr);
-
- // early termination if vendor service cannot fully prepare model
- if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
- ASSERT_EQ(nullptr, preparedModel->get());
- LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
- "prepare model that it does not support.";
- std::cout << "[ ] Early termination of test because vendor service cannot "
- "prepare model that it does not support."
- << std::endl;
- return;
+void Execute(const sp<IDevice>& device, const TestModel& testModel, bool testDynamicOutputShape) {
+ Model model = createModel(testModel);
+ if (testDynamicOutputShape) {
+ makeOutputDimensionsUnspecified(&model);
}
- EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
- ASSERT_NE(nullptr, preparedModel->get());
+
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
+
+ EvaluatePreparedModel(preparedModel, testModel, testDynamicOutputShape);
+}
+
+void GeneratedTestBase::SetUp() {
+ testing::TestWithParam<GeneratedTestParam>::SetUp();
+ ASSERT_NE(kDevice, nullptr);
+}
+
+std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
+ return TestModelManager::get().getTestModels(filter);
+}
+
+std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info) {
+ const auto& [namedDevice, namedModel] = info.param;
+ return gtestCompliantName(getName(namedDevice) + "_" + getName(namedModel));
}
// Tag for the generated tests
-class GeneratedTest : public GeneratedTestBase {
- protected:
- void Execute(const TestModel& testModel, bool testDynamicOutputShape) {
- Model model = createModel(testModel);
- if (testDynamicOutputShape) {
- makeOutputDimensionsUnspecified(&model);
- }
-
- sp<IPreparedModel> preparedModel = nullptr;
- PrepareModel(device, model, &preparedModel);
- if (preparedModel == nullptr) {
- GTEST_SKIP();
- }
- EvaluatePreparedModel(preparedModel, testModel, testDynamicOutputShape);
- }
-};
+class GeneratedTest : public GeneratedTestBase {};
// Tag for the dynamic output shape tests
class DynamicOutputShapeTest : public GeneratedTest {};
TEST_P(GeneratedTest, Test) {
- Execute(*mTestModel, /*testDynamicOutputShape=*/false);
+ Execute(kDevice, kTestModel, /*testDynamicOutputShape=*/false);
}
TEST_P(DynamicOutputShapeTest, Test) {
- Execute(*mTestModel, /*testDynamicOutputShape=*/true);
+ Execute(kDevice, kTestModel, /*testDynamicOutputShape=*/true);
}
INSTANTIATE_GENERATED_TEST(GeneratedTest,
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
index cb01b91..dfc980c 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
@@ -22,50 +22,43 @@
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <functional>
#include <vector>
+#include "1.0/Utils.h"
#include "TestHarness.h"
#include "VtsHalNeuralnetworks.h"
namespace android::hardware::neuralnetworks::V1_2::vts::functional {
-class GeneratedTestBase
- : public NeuralnetworksHidlTest,
- public ::testing::WithParamInterface<test_helper::TestModelManager::TestParam> {
- protected:
- void SetUp() override {
- NeuralnetworksHidlTest::SetUp();
- ASSERT_NE(mTestModel, nullptr);
- }
+using NamedModel = Named<const test_helper::TestModel*>;
+using GeneratedTestParam = std::tuple<NamedDevice, NamedModel>;
- const test_helper::TestModel* mTestModel = GetParam().second;
+class GeneratedTestBase : public testing::TestWithParam<GeneratedTestParam> {
+ protected:
+ void SetUp() override;
+ const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
+ const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
};
-#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
- INSTANTIATE_TEST_SUITE_P( \
- TestGenerated, TestSuite, \
- ::testing::ValuesIn(::test_helper::TestModelManager::get().getTestModels(filter)), \
- [](const auto& info) { return info.param.first; })
+using FilterFn = std::function<bool(const test_helper::TestModel&)>;
+std::vector<NamedModel> getNamedModels(const FilterFn& filter);
+
+std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
+
+#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
+ INSTANTIATE_TEST_SUITE_P(TestGenerated, TestSuite, \
+ testing::Combine(testing::ValuesIn(getNamedDevices()), \
+ testing::ValuesIn(getNamedModels(filter))), \
+ printGeneratedTest)
// Tag for the validation tests, instantiated in VtsHalNeuralnetworks.cpp.
// TODO: Clean up the hierarchy for ValidationTest.
-class ValidationTest : public GeneratedTestBase {
- protected:
- void validateEverything(const Model& model, const V1_0::Request& request);
- void validateFailure(const Model& model, const V1_0::Request& request);
+class ValidationTest : public GeneratedTestBase {};
- private:
- void validateModel(const Model& model);
- void validateRequest(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
- void validateRequestFailure(const sp<IPreparedModel>& preparedModel,
- const V1_0::Request& request);
- void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
-};
-
-Model createModel(const ::test_helper::TestModel& testModel);
+Model createModel(const test_helper::TestModel& testModel);
void PrepareModel(const sp<IDevice>& device, const Model& model, sp<IPreparedModel>* preparedModel);
void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel,
- const ::test_helper::TestModel& testModel, bool testDynamicOutputShape);
+ const test_helper::TestModel& testModel, bool testDynamicOutputShape);
} // namespace android::hardware::neuralnetworks::V1_2::vts::functional
diff --git a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
index 844e879..1d4493d 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
@@ -262,7 +262,7 @@
}));
// serialize the request
- const auto serialized = ::android::nn::serialize(request, MeasureTiming::YES, slots);
+ const auto serialized = android::nn::serialize(request, MeasureTiming::YES, slots);
// validations
removeDatumTest(sender.get(), receiver.get(), serialized);
@@ -299,7 +299,7 @@
// skip test if regular burst output isn't useful for testing a failure
// caused by having too small of a length for the result FMQ
const std::vector<FmqResultDatum> serialized =
- ::android::nn::serialize(statusRegular, outputShapesRegular, timingRegular);
+ android::nn::serialize(statusRegular, outputShapesRegular, timingRegular);
if (statusRegular != ErrorStatus::NONE ||
serialized.size() <= kExecutionBurstChannelSmallLength) {
return;
@@ -391,8 +391,7 @@
///////////////////////////// ENTRY POINT //////////////////////////////////
-void ValidationTest::validateBurst(const sp<IPreparedModel>& preparedModel,
- const Request& request) {
+void validateBurst(const sp<IPreparedModel>& preparedModel, const Request& request) {
ASSERT_NO_FATAL_FAILURE(validateBurstSerialization(preparedModel, request));
ASSERT_NO_FATAL_FAILURE(validateBurstFmqLength(preparedModel, request));
ASSERT_NO_FATAL_FAILURE(validateBurstSanitized(preparedModel, request));
diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
index ea9aa4f..a14b86b 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
@@ -692,7 +692,7 @@
////////////////////////// ENTRY POINT //////////////////////////////
-void ValidationTest::validateModel(const Model& model) {
+void validateModel(const sp<IDevice>& device, const Model& model) {
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
index 684b433..f25ee62 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
@@ -94,7 +94,7 @@
// create burst
std::shared_ptr<::android::nn::ExecutionBurstController> burst =
- ::android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
+ android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
ASSERT_NE(nullptr, burst.get());
// create memory keys
@@ -148,14 +148,12 @@
///////////////////////////// ENTRY POINT //////////////////////////////////
-void ValidationTest::validateRequest(const sp<IPreparedModel>& preparedModel,
- const Request& request) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
removeInputTest(preparedModel, request);
removeOutputTest(preparedModel, request);
}
-void ValidationTest::validateRequestFailure(const sp<IPreparedModel>& preparedModel,
- const Request& request) {
+void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeSynchronously]");
Return<void> executeStatus = preparedModel->executeSynchronously(
request, MeasureTiming::NO,
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
index ea9d684..4fbd0e2 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
@@ -17,13 +17,15 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
#include "VtsHalNeuralnetworks.h"
+#include <android-base/logging.h>
+#include <hidl/ServiceManagement.h>
+#include <string>
+#include <utility>
#include "1.0/Callbacks.h"
#include "1.0/Utils.h"
#include "GeneratedTestHarness.h"
#include "TestHarness.h"
-#include <android-base/logging.h>
-
namespace android::hardware::neuralnetworks::V1_2::vts::functional {
using implementation::PreparedModelCallback;
@@ -33,24 +35,25 @@
using V1_1::ExecutionPreference;
// internal helper function
-static void createPreparedModel(const sp<IDevice>& device, const Model& model,
- sp<IPreparedModel>* preparedModel) {
+void createPreparedModel(const sp<IDevice>& device, const Model& model,
+ sp<IPreparedModel>* preparedModel) {
ASSERT_NE(nullptr, preparedModel);
+ *preparedModel = nullptr;
// see if service can handle model
bool fullySupportsModel = false;
- Return<void> supportedOpsLaunchStatus = device->getSupportedOperations_1_2(
+ const Return<void> supportedCall = device->getSupportedOperations_1_2(
model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
ASSERT_EQ(ErrorStatus::NONE, status);
ASSERT_NE(0ul, supported.size());
fullySupportsModel = std::all_of(supported.begin(), supported.end(),
[](bool valid) { return valid; });
});
- ASSERT_TRUE(supportedOpsLaunchStatus.isOk());
+ ASSERT_TRUE(supportedCall.isOk());
// launch prepare model
- sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_2(
+ const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+ const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_2(
model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
@@ -58,7 +61,7 @@
// retrieve prepared model
preparedModelCallback->wait();
- ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+ const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
*preparedModel = getPreparedModel_1_2(preparedModelCallback);
// The getSupportedOperations_1_2 call returns a list of operations that are
@@ -70,84 +73,91 @@
// can continue.
if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
ASSERT_EQ(nullptr, preparedModel->get());
- LOG(INFO) << "NN VTS: Unable to test Request validation because vendor service cannot "
- "prepare model that it does not support.";
- std::cout << "[ ] Unable to test Request validation because vendor service "
- "cannot prepare model that it does not support."
+ LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot prepare "
+ "model that it does not support.";
+ std::cout << "[ ] Early termination of test because vendor service cannot "
+ "prepare model that it does not support."
<< std::endl;
- return;
+ GTEST_SKIP();
}
ASSERT_EQ(ErrorStatus::NONE, prepareReturnStatus);
ASSERT_NE(nullptr, preparedModel->get());
}
-// A class for test environment setup
-NeuralnetworksHidlEnvironment* NeuralnetworksHidlEnvironment::getInstance() {
- // This has to return a "new" object because it is freed inside
- // ::testing::AddGlobalTestEnvironment when the gtest is being torn down
- static NeuralnetworksHidlEnvironment* instance = new NeuralnetworksHidlEnvironment();
- return instance;
-}
-
-void NeuralnetworksHidlEnvironment::registerTestServices() {
- registerTestService<IDevice>();
-}
-
-// The main test class for NEURALNETWORK HIDL HAL.
void NeuralnetworksHidlTest::SetUp() {
- ::testing::VtsHalHidlTargetTestBase::SetUp();
-
-#ifdef PRESUBMIT_NOT_VTS
- const std::string name =
- NeuralnetworksHidlEnvironment::getInstance()->getServiceName<IDevice>();
- const std::string sampleDriver = "sample-";
- if (device == nullptr && name.substr(0, sampleDriver.size()) == sampleDriver) {
- GTEST_SKIP();
- }
-#endif // PRESUBMIT_NOT_VTS
-
- ASSERT_NE(nullptr, device.get());
+ testing::TestWithParam<NeuralnetworksHidlTestParam>::SetUp();
+ ASSERT_NE(kDevice, nullptr);
}
-void NeuralnetworksHidlTest::TearDown() {
- ::testing::VtsHalHidlTargetTestBase::TearDown();
+static NamedDevice makeNamedDevice(const std::string& name) {
+ return {name, IDevice::getService(name)};
}
-void ValidationTest::validateEverything(const Model& model, const Request& request) {
- validateModel(model);
+static std::vector<NamedDevice> getNamedDevicesImpl() {
+ // Retrieves the name of all service instances that implement IDevice,
+ // including any Lazy HAL instances.
+ const std::vector<std::string> names = hardware::getAllHalInstanceNames(IDevice::descriptor);
- // create IPreparedModel
+ // Get a handle to each device and pair it with its name.
+ std::vector<NamedDevice> namedDevices;
+ namedDevices.reserve(names.size());
+ std::transform(names.begin(), names.end(), std::back_inserter(namedDevices), makeNamedDevice);
+ return namedDevices;
+}
+
+const std::vector<NamedDevice>& getNamedDevices() {
+ const static std::vector<NamedDevice> devices = getNamedDevicesImpl();
+ return devices;
+}
+
+std::string printNeuralnetworksHidlTest(
+ const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info) {
+ return gtestCompliantName(getName(info.param));
+}
+
+INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
+
+// Forward declaration from ValidateModel.cpp
+void validateModel(const sp<IDevice>& device, const Model& model);
+// Forward declaration from ValidateRequest.cpp
+void validateRequest(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
+// Forward declaration from ValidateRequest.cpp
+void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
+// Forward declaration from ValidateBurst.cpp
+void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
+
+void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
+ validateModel(device, model);
+
+ // Create IPreparedModel.
sp<IPreparedModel> preparedModel;
- ASSERT_NO_FATAL_FAILURE(createPreparedModel(device, model, &preparedModel));
- if (preparedModel == nullptr) {
- return;
- }
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
validateRequest(preparedModel, request);
validateBurst(preparedModel, request);
}
-void ValidationTest::validateFailure(const Model& model, const Request& request) {
+void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request) {
// TODO: Should this always succeed?
// What if the invalid input is part of the model (i.e., a parameter).
- validateModel(model);
+ validateModel(device, model);
+ // Create IPreparedModel.
sp<IPreparedModel> preparedModel;
- ASSERT_NO_FATAL_FAILURE(createPreparedModel(device, model, &preparedModel));
- if (preparedModel == nullptr) {
- return;
- }
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
validateRequestFailure(preparedModel, request);
}
TEST_P(ValidationTest, Test) {
- const Model model = createModel(*mTestModel);
- const Request request = createRequest(*mTestModel);
- if (mTestModel->expectFailure) {
- validateFailure(model, request);
+ const Model model = createModel(kTestModel);
+ const Request request = createRequest(kTestModel);
+ if (kTestModel.expectFailure) {
+ validateFailure(kDevice, model, request);
} else {
- validateEverything(model, request);
+ validateEverything(kDevice, model, request);
}
}
@@ -159,26 +169,3 @@
}
} // namespace android::hardware::neuralnetworks::V1_2::vts::functional
-
-namespace android::hardware::neuralnetworks::V1_0 {
-
-::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
- return os << toString(errorStatus);
-}
-
-::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus) {
- return os << toString(deviceStatus);
-}
-
-} // namespace android::hardware::neuralnetworks::V1_0
-
-using android::hardware::neuralnetworks::V1_2::vts::functional::NeuralnetworksHidlEnvironment;
-
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(NeuralnetworksHidlEnvironment::getInstance());
- ::testing::InitGoogleTest(&argc, argv);
- NeuralnetworksHidlEnvironment::getInstance()->init(&argc, argv);
-
- int status = RUN_ALL_TESTS();
- return status;
-}
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
index 4a6d33b..d01336e 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
@@ -17,58 +17,41 @@
#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_2_VTS_HAL_NEURALNETWORKS_H
#define ANDROID_HARDWARE_NEURALNETWORKS_V1_2_VTS_HAL_NEURALNETWORKS_H
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
-#include <android-base/macros.h>
-#include <android/hardware/neuralnetworks/1.0/types.h>
-#include <android/hardware/neuralnetworks/1.1/types.h>
#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <gtest/gtest.h>
-
-#include <iostream>
-#include <vector>
-
+#include "1.0/Utils.h"
#include "1.2/Callbacks.h"
-#include "TestHarness.h"
namespace android::hardware::neuralnetworks::V1_2::vts::functional {
-// A class for test environment setup
-class NeuralnetworksHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- DISALLOW_COPY_AND_ASSIGN(NeuralnetworksHidlEnvironment);
- NeuralnetworksHidlEnvironment() = default;
+using NamedDevice = Named<sp<IDevice>>;
+using NeuralnetworksHidlTestParam = NamedDevice;
- public:
- static NeuralnetworksHidlEnvironment* getInstance();
- void registerTestServices() override;
-};
-
-// The main test class for NEURALNETWORKS HIDL HAL.
-class NeuralnetworksHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- DISALLOW_COPY_AND_ASSIGN(NeuralnetworksHidlTest);
-
- public:
- NeuralnetworksHidlTest() = default;
- void SetUp() override;
- void TearDown() override;
-
+class NeuralnetworksHidlTest : public testing::TestWithParam<NeuralnetworksHidlTestParam> {
protected:
- const sp<IDevice> device = ::testing::VtsHalHidlTargetTestBase::getService<IDevice>(
- NeuralnetworksHidlEnvironment::getInstance());
+ void SetUp() override;
+ const sp<IDevice> kDevice = getData(GetParam());
};
+const std::vector<NamedDevice>& getNamedDevices();
+
+std::string printNeuralnetworksHidlTest(
+ const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info);
+
+#define INSTANTIATE_DEVICE_TEST(TestSuite) \
+ INSTANTIATE_TEST_SUITE_P(PerInstance, TestSuite, testing::ValuesIn(getNamedDevices()), \
+ printNeuralnetworksHidlTest)
+
+// Create an IPreparedModel object. If the model cannot be prepared,
+// "preparedModel" will be nullptr instead.
+void createPreparedModel(const sp<IDevice>& device, const Model& model,
+ sp<IPreparedModel>* preparedModel);
+
// Utility function to get PreparedModel from callback and downcast to V1_2.
sp<IPreparedModel> getPreparedModel_1_2(const sp<implementation::PreparedModelCallback>& callback);
} // namespace android::hardware::neuralnetworks::V1_2::vts::functional
-namespace android::hardware::neuralnetworks::V1_0 {
-
-// pretty-print values for error messages
-::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
-::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus);
-
-} // namespace android::hardware::neuralnetworks::V1_0
-
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_2_VTS_HAL_NEURALNETWORKS_H
diff --git a/neuralnetworks/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
index 50b6c19..421922a 100644
--- a/neuralnetworks/TEST_MAPPING
+++ b/neuralnetworks/TEST_MAPPING
@@ -1,26 +1,35 @@
{
"presubmit": [
{
- "name": "PresubmitHalNeuralnetworksV1_0TargetTest",
+ "name": "VtsHalNeuralnetworksV1_0TargetTest",
"options": [
{
- "native-test-flag": "--hal_service_instance=android.hardware.neuralnetworks@1.0::IDevice/sample-all"
+ // Just use sample-all driver for presubmit tests for faster results.
+ // The other sample drivers (fast-float, quant, etc.) are subsets of
+ // sample-all.
+ "native-test-flag": "--gtest_filter=*sample_all*"
}
]
},
{
- "name": "PresubmitHalNeuralnetworksV1_1TargetTest",
+ "name": "VtsHalNeuralnetworksV1_1TargetTest",
"options": [
{
- "native-test-flag": "--hal_service_instance=android.hardware.neuralnetworks@1.1::IDevice/sample-all"
+ // Just use sample-all driver for presubmit tests for faster results.
+ // The other sample drivers (fast-float, quant, etc.) are subsets of
+ // sample-all.
+ "native-test-flag": "--gtest_filter=*sample_all*"
}
]
},
{
- "name": "PresubmitHalNeuralnetworksV1_2TargetTest",
+ "name": "VtsHalNeuralnetworksV1_2TargetTest",
"options": [
{
- "native-test-flag": "--hal_service_instance=android.hardware.neuralnetworks@1.2::IDevice/sample-all"
+ // Just use sample-all driver for presubmit tests for faster results.
+ // The other sample drivers (fast-float, quant, etc.) are subsets of
+ // sample-all.
+ "native-test-flag": "--gtest_filter=*sample_all*"
}
]
}
diff --git a/power/stats/1.0/default/PowerStats.cpp b/power/stats/1.0/default/PowerStats.cpp
index 78766f2..68275ce 100644
--- a/power/stats/1.0/default/PowerStats.cpp
+++ b/power/stats/1.0/default/PowerStats.cpp
@@ -87,7 +87,7 @@
std::string railFileName;
std::string spsFileName;
uint32_t index = 0;
- uint32_t samplingRate;
+ unsigned long samplingRate;
for (const auto& path : mPm.devicePaths) {
railFileName = path + "/enabled_rails";
spsFileName = path + "/sampling_rate";
@@ -109,10 +109,11 @@
while (std::getline(railNames, line)) {
std::vector<std::string> words = android::base::Split(line, ":");
if (words.size() == 2) {
- mPm.railsInfo.emplace(words[0], RailData{.devicePath = path,
- .index = index,
- .subsysName = words[1],
- .samplingRate = samplingRate});
+ mPm.railsInfo.emplace(
+ words[0], RailData{.devicePath = path,
+ .index = index,
+ .subsysName = words[1],
+ .samplingRate = static_cast<uint32_t>(samplingRate)});
index++;
} else {
ALOGW("Unexpected format in file: %s", railFileName.c_str());
diff --git a/radio/1.2/types.hal b/radio/1.2/types.hal
index dffebd3..f10d753 100644
--- a/radio/1.2/types.hal
+++ b/radio/1.2/types.hal
@@ -161,7 +161,8 @@
ScanType type;
/**
- * Time interval in seconds between periodic scans, only valid when type = PERIODIC
+ * Time interval in seconds between the completion of one scan and the start of a subsequent scan.
+ * This field is only valid when 'type' is 'PERIODIC'.
* Range: ScanIntervalRange:MIN to ScanIntervalRange:MAX
*/
int32_t interval;
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
index 26f2c90..b2d19a2 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
@@ -381,7 +381,7 @@
.interval = 60,
.specifiers = {specifier},
.maxSearchTime = 600,
- .incrementalResults = false,
+ .incrementalResults = true,
.incrementalResultsPeriodicity = 0};
Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
@@ -416,7 +416,7 @@
.interval = 60,
.specifiers = {specifier},
.maxSearchTime = 600,
- .incrementalResults = false,
+ .incrementalResults = true,
.incrementalResultsPeriodicity = 11};
Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
diff --git a/tests/baz/1.0/IBaz.hal b/tests/baz/1.0/IBaz.hal
index 91ed1f2..7855446 100644
--- a/tests/baz/1.0/IBaz.hal
+++ b/tests/baz/1.0/IBaz.hal
@@ -29,6 +29,11 @@
VALL = V0 | V1 | V2 | V3,
};
+ struct BitFieldTester {
+ bitfield<BitField> scalar;
+ vec<bitfield<BitField>> vector;
+ };
+
enum SomeOtherEnum : uint8_t {
bar = 66
};
@@ -108,6 +113,7 @@
haveSomeStrings(string[3] array) generates (string[2] result);
haveAStringVec(vec<string> vector) generates (vec<string> result);
+ repeatBitfieldVec(vec<bitfield<BitField>> vector) generates (vec<bitfield<BitField>> result);
returnABunchOfStrings() generates (string a, string b, string c);
diff --git a/tests/baz/1.0/default/Baz.cpp b/tests/baz/1.0/default/Baz.cpp
index e118122..2ce096c 100644
--- a/tests/baz/1.0/default/Baz.cpp
+++ b/tests/baz/1.0/default/Baz.cpp
@@ -364,6 +364,12 @@
return Void();
}
+Return<void> Baz::repeatBitfieldVec(const hidl_vec<uint8_t>& vector,
+ repeatBitfieldVec_cb _hidl_cb) {
+ _hidl_cb(vector);
+ return Void();
+}
+
Return<void> Baz::returnABunchOfStrings(returnABunchOfStrings_cb _hidl_cb) {
hidl_string eins; eins = "Eins";
hidl_string zwei; zwei = "Zwei";
diff --git a/tests/baz/1.0/default/Baz.h b/tests/baz/1.0/default/Baz.h
index c264f47..1e24d52 100644
--- a/tests/baz/1.0/default/Baz.h
+++ b/tests/baz/1.0/default/Baz.h
@@ -86,6 +86,8 @@
haveSomeStrings_cb _hidl_cb) override;
Return<void> haveAStringVec(const hidl_vec<hidl_string>& vector,
haveAStringVec_cb _hidl_cb) override;
+ Return<void> repeatBitfieldVec(const hidl_vec<uint8_t>& vector,
+ repeatBitfieldVec_cb _hidl_cb) override;
Return<void> returnABunchOfStrings(returnABunchOfStrings_cb _hidl_cb) override;
Return<uint8_t> returnABitField() override;
Return<uint32_t> size(uint32_t size) override;
diff --git a/tv/tuner/1.0/Android.bp b/tv/tuner/1.0/Android.bp
index 986518b..09265f7 100644
--- a/tv/tuner/1.0/Android.bp
+++ b/tv/tuner/1.0/Android.bp
@@ -13,6 +13,7 @@
"IDescrambler.hal",
"IFrontend.hal",
"IFrontendCallback.hal",
+ "ILnb.hal",
"ITuner.hal",
],
interfaces: [
diff --git a/tv/tuner/1.0/IDemux.hal b/tv/tuner/1.0/IDemux.hal
index 2d7b275..7ead34b 100644
--- a/tv/tuner/1.0/IDemux.hal
+++ b/tv/tuner/1.0/IDemux.hal
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.hardware.tv.tuner@1.0;
import IDemuxCallback;
@@ -180,5 +196,220 @@
* UNKNOWN_ERROR if failed for other reasons.
*/
close() generates (Result result);
-};
+ /**
+ * Add output to the demux
+ *
+ * It is used by the client to record output data from selected filters.
+ *
+ * @param bufferSize the buffer size of the output to be added. It's used to
+ * create a FMQ(Fast Message Queue) to hold data from selected filters.
+ * @param cb the callback for the demux to be used to send notifications
+ * back to the client.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * OUT_OF_MEMORY if failed for not enough memory.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ addOutput(uint32_t bufferSize, IDemuxCallback cb) generates (Result result);
+
+ /**
+ * Get the descriptor of the output's FMQ
+ *
+ * It is used by the client to get the descriptor of the output's Fast
+ * Message Queue. The data in FMQ is muxed packets output from selected
+ * filters. The packet's format is specifed by DemuxDataFormat in
+ * DemuxOutputSettings.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if failed for other reasons.
+ * @return queue the descriptor of the output's FMQ
+ */
+ getOutputQueueDesc() generates (Result result, fmq_sync<uint8_t> queue);
+
+ /**
+ * Configure the demux's output.
+ *
+ * It is used by the client to configure the demux's output for recording.
+ *
+ * @param settings the settings of the demux's output.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ configureOutput(DemuxOutputSettings settings) generates (Result result);
+
+ /**
+ * Attach one filter to the demux's output.
+ *
+ * It is used by the client to mux one filter's output to demux's output.
+ *
+ * @param filterId the ID of the attached filter.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ attachOutputFilter(DemuxFilterId filterId) generates (Result result);
+
+ /**
+ * Detach one filter from the demux's output.
+ *
+ * It is used by the client to remove one filter's output from demux's
+ * output.
+ *
+ * @param filterId the ID of the detached filter.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ detachOutputFilter(DemuxFilterId filterId) generates (Result result);
+
+ /**
+ * Start to take data to the demux's output.
+ *
+ * It is used by the client to ask the output to start to take data from
+ * attached filters.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ startOutput() generates (Result result);
+
+ /**
+ * Stop to take data to the demux's output.
+ *
+ * It is used by the client to ask the output to stop to take data from
+ * attached filters.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ stopOutput() generates (Result result);
+
+ /**
+ * Flush unconsumed data in the demux's output.
+ *
+ * It is used by the client to ask the demux to flush the data which is
+ * already produced but not consumed yet in the demux's output.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ flushOutput() generates (Result result);
+
+ /**
+ * Remove the demux's output.
+ *
+ * It is used by the client to remove the demux's output.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ removeOutput() generates (Result result);
+
+ /**
+ * Add input to the demux
+ *
+ * It is used by the client to add the demux's input for playback content.
+ *
+ * @param bufferSize the buffer size of the demux's input to be added.
+ * It's used to create a FMQ(Fast Message Queue) to hold input data.
+ * @param cb the callback for the demux to be used to send notifications
+ * back to the client.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * OUT_OF_MEMORY if failed for not enough memory.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ addInput(uint32_t bufferSize, IDemuxCallback cb) generates (Result result);
+
+ /**
+ * Get the descriptor of the input's FMQ
+ *
+ * It is used by the client to get the descriptor of the input's Fast
+ * Message Queue. The data in FMQ is fed by client. Data format is specifed
+ * by DemuxDataFormat in DemuxInputSettings.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if failed for other reasons.
+ * @return queue the descriptor of the output's FMQ
+ */
+ getInputQueueDesc() generates (Result result, fmq_sync<uint8_t> queue);
+
+ /**
+ * Configure the demux's input.
+ *
+ * It is used by the client to configure the demux's input for playback.
+ *
+ * @param settings the settings of the demux's input.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ configureInput(DemuxInputSettings settings) generates (Result result);
+
+ /**
+ * Start to consume the data from the demux's input.
+ *
+ * It is used by the client to ask the demux to start to consume data from
+ * the demux's input.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ startInput() generates (Result result);
+
+ /**
+ * Stop to consume the data from the demux's input.
+ *
+ * It is used by the client to ask the demux to stop to consume data from
+ * the demux's input.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ stopInput() generates (Result result);
+
+ /**
+ * Flush unconsumed data in the demux's input.
+ *
+ * It is used by the client to ask the demux to flush the data which is
+ * already produced but not consumed yet in the demux's input.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ flushInput() generates (Result result);
+
+ /**
+ * Remove the demux's input.
+ *
+ * It is used by the client to remove the demux's input.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if failed for wrong state.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ removeInput() generates (Result result);
+};
diff --git a/tv/tuner/1.0/IDemuxCallback.hal b/tv/tuner/1.0/IDemuxCallback.hal
index 7efd2c3..7bce9ef 100644
--- a/tv/tuner/1.0/IDemuxCallback.hal
+++ b/tv/tuner/1.0/IDemuxCallback.hal
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.hardware.tv.tuner@1.0;
interface IDemuxCallback {
@@ -15,5 +31,19 @@
* @param status a new status of the demux filter.
*/
oneway onFilterStatus(DemuxFilterId filterId, DemuxFilterStatus status);
+
+ /**
+ * Notify the client a new status of the demux's output.
+ *
+ * @param status a new status of the demux's output.
+ */
+ oneway onOutputStatus(DemuxOutputStatus status);
+
+ /**
+ * Notify the client a new status of the demux's input.
+ *
+ * @param status a new status of the demux's input.
+ */
+ oneway onInputStatus(DemuxInputStatus status);
};
diff --git a/tv/tuner/1.0/IDescrambler.hal b/tv/tuner/1.0/IDescrambler.hal
index d078657..61ff1df 100644
--- a/tv/tuner/1.0/IDescrambler.hal
+++ b/tv/tuner/1.0/IDescrambler.hal
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.hardware.tv.tuner@1.0;
/**
* Descrambler is used to descramble input data.
diff --git a/tv/tuner/1.0/IFrontend.hal b/tv/tuner/1.0/IFrontend.hal
index 05cee91..962e4ba 100644
--- a/tv/tuner/1.0/IFrontend.hal
+++ b/tv/tuner/1.0/IFrontend.hal
@@ -16,6 +16,7 @@
package android.hardware.tv.tuner@1.0;
import IFrontendCallback;
+import ILnb;
/**
* A Tuner Frontend is used to tune to a frequency and lock signal. It provide
@@ -79,4 +80,80 @@
* UNKNOWN_ERROR if failed for other reasons.
*/
close() generates (Result result);
+
+ /**
+ * Scan the frontend to use the settings given.
+ *
+ * This uses the frontend to start a scan from signal delivery information.
+ * If previous scan isn't completed, this call MUST stop previous scan,
+ * and start a new scan.
+ * Scan is an async call, with FrontendScanMessage sent via callback.
+ *
+ * @param settings Signal delivery information which the frontend uses to
+ * scan the signal.
+ * @param type the type which the frontend uses to scan the signal.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if tuning can't be applied at current stage,
+ * UNKNOWN_ERROR if tuning failed for other reasons.
+ */
+ scan(FrontendSettings settings, FrontendScanType type) generates (Result result);
+
+ /**
+ * Stops a previous scanning.
+ *
+ * If the method completes successfully, the frontend stop previous
+ * scanning.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successfully stop tuning.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ stopScan() generates (Result result);
+
+ /**
+ * Gets the statuses of the frontend.
+ *
+ * This retrieve the statuses of the frontend for given status types.
+ *
+ * @param statusTypes an array of status type which the caller request.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if tuning can't be applied at current stage,
+ * UNKNOWN_ERROR if tuning failed for other reasons.
+ * @return statuses an array of statuses which response the caller's
+ * request.
+ */
+ getStatus(vec<FrontendStatusType> statusTypes) generates (Result result, vec<FrontendStatus> statuses);
+
+ /**
+ * Sets Low-Noise Block downconverter (LNB) for satellite frontend.
+ *
+ * This assigns a hardware LNB resource to the satellite frontend. It can be
+ * called multiple times to update LNB assignment. The LNB resource must be
+ * released when the frontend is closed.
+ *
+ * @param lnbId the Id of assigned LNB resource.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if the frontend can't be set with a LNB, such as
+ * cable frontend.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ setLnb(LnbId lnbId) generates (Result result);
+
+ /**
+ * Enble or Disable Low Noise Amplifier (LNA).
+ *
+ * @param bEnable true if activate LNA module; false if deactivate LNA
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if the frontend doesn't support LNA.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ setLna(bool bEnable) generates (Result result);
};
diff --git a/tv/tuner/1.0/IFrontendCallback.hal b/tv/tuner/1.0/IFrontendCallback.hal
index e907049..8896a09 100644
--- a/tv/tuner/1.0/IFrontendCallback.hal
+++ b/tv/tuner/1.0/IFrontendCallback.hal
@@ -33,5 +33,13 @@
* Specification Version 4.2.
*/
oneway onDiseqcMessage(vec<uint8_t> diseqcMessage);
-};
+ /**
+ * The callback function that must be called by HAL implementation to notify
+ * the client of scan messages.
+ *
+ * @param type the type of scan message.
+ * @param message the scan message sent by HAL to the client.
+ */
+ oneway onScanMessage(FrontendScanMessageType type, FrontendScanMessage message);
+};
diff --git a/tv/tuner/1.0/ILnb.hal b/tv/tuner/1.0/ILnb.hal
new file mode 100644
index 0000000..6b7119e
--- /dev/null
+++ b/tv/tuner/1.0/ILnb.hal
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.tv.tuner@1.0;
+
+/**
+ * A Tuner LNB (low-noise block downconverter) is used by satellite frontend
+ * to receive the microwave signal from the satellite, amplify it, and
+ * downconvert the frequency to a lower frequency.
+ */
+interface ILnb {
+ /**
+ * Set the lnb's power voltage.
+ *
+ * @param voltage the power's voltage the Lnb to use.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_ARGUMENT if the selected voltage isn't allowed,
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ setVoltage(FrontendLnbVoltage voltage) generates (Result result);
+
+ /**
+ * Set the lnb's tone mode.
+ *
+ * @param tone the tone mode the Lnb to use.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_ARGUMENT if the selected tone mode isn't allowed,
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ setTone(FrontendLnbTone tone) generates (Result result);
+
+ /**
+ * Select the lnb's position.
+ *
+ * @param position the position the Lnb to use.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_ARGUMENT if the selected position isn't allowed,
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ setSatellitePosition(FrontendLnbPosition position) generates (Result result);
+
+ /**
+ * Sends DiSEqC (Digital Satellite Equipment Control) message.
+ *
+ * Client sends DiSeqc message to DiSEqc to LNB. The response message from
+ * the device comes back to the client through frontend's callback
+ * onDiseqcMessage.
+ *
+ * @param diseqcMessage a byte array of data for DiSEqC message which is
+ * specified by EUTELSAT Bus Functional Specification Version 4.2.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if the frontend can't send DiSEqc Message, such as
+ * cable frontend.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ sendDiseqcMessage(vec<uint8_t> diseqcMessage) generates (Result result);
+
+ /**
+ * Releases the LNB instance
+ *
+ * Associated resources are released. close may be called more than once.
+ * Calls to any other method after this will return an error
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ close() generates (Result result);
+};
diff --git a/tv/tuner/1.0/ITuner.hal b/tv/tuner/1.0/ITuner.hal
index a0f3e8e..0d63442 100644
--- a/tv/tuner/1.0/ITuner.hal
+++ b/tv/tuner/1.0/ITuner.hal
@@ -19,6 +19,7 @@
import IDemux;
import IDescrambler;
import IFrontend;
+import ILnb;
/**
* Top level interface to manage Frontend, Demux and Decrambler hardware
@@ -45,6 +46,7 @@
* @param frontendId the id of the frontend to be opened.
* @return result Result status of the operation.
* SUCCESS if successful,
+ * UNAVAILABLE if no resource.
* UNKNOWN_ERROR if creation failed for other reasons.
* @return frontend the newly created frontend interface.
*/
@@ -62,10 +64,20 @@
* @return demuxId newly created demux id.
* @return demux the newly created demux interface.
*/
- openDemux()
+ openDemux()
generates (Result result, DemuxId demuxId, IDemux demux);
/**
+ * Retrieve the Demux's Capabilities.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if the inquiry failed for other reasons.
+ * @return caps the Demux's Capabilities.
+ */
+ getDemuxCaps() generates (Result result, DemuxCapabilities caps);
+
+ /**
* Create a new instance of Descrambler.
*
* It is used by the client to create a Descrambler instance.
@@ -75,6 +87,46 @@
* UNKNOWN_ERROR if creation failed for other reasons.
* @return descrambler the newly created descrambler interface.
*/
- openDescrambler()
+ openDescrambler()
generates (Result result, IDescrambler descrambler);
+
+ /**
+ * Retrieve the frontend's information.
+ *
+ * @param frontendId the id of the frontend to be inquiried.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if the inquiry failed for other reasons.
+ * @return info the frontend's information.
+ */
+ getFrontendInfo(FrontendId frontendId)
+ generates (Result result, FrontendInfo info);
+
+ /**
+ * Get low-noise block downconverter (LNB) IDs.
+ *
+ * It is used by the client to get all available LNBs' IDs.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if tuning failed for other reasons.
+ * @return frontendIds an array of LnbId for the available LNBs.
+ */
+ getLnbIds() generates (Result result, vec<LnbId> lnbIds);
+
+ /**
+ * Create a new instance of Lnb given a lnbId.
+ *
+ * It is used by the client to create a Lnb instance for satellite Frontend.
+ *
+ * @param lnbId the id of the LNB to be opened.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNAVAILABLE if no resource.
+ * UNKNOWN_ERROR if creation failed for other reasons.
+ * @return lnb the newly created Lnb interface.
+ */
+ openLnbById(LnbId lnbId)
+ generates (Result result, ILnb lnb);
};
+
diff --git a/tv/tuner/1.0/default/Android.bp b/tv/tuner/1.0/default/Android.bp
index b211dd2..0ae8bcd 100644
--- a/tv/tuner/1.0/default/Android.bp
+++ b/tv/tuner/1.0/default/Android.bp
@@ -8,6 +8,7 @@
"Descrambler.cpp",
"Demux.cpp",
"Tuner.cpp",
+ "Lnb.cpp",
"service.cpp",
],
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index 4016c5a..04382b0 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -67,69 +67,74 @@
0x73, 0x63, 0x65, 0x6e, 0x65,
};
-Demux::Demux(uint32_t demuxId) {
+Demux::Demux(uint32_t demuxId, sp<Tuner> tuner) {
mDemuxId = demuxId;
+ mTunerService = tuner;
}
Demux::~Demux() {}
-bool Demux::createAndSaveMQ(uint32_t bufferSize, uint32_t filterId) {
- ALOGV("%s", __FUNCTION__);
-
- // Create a synchronized FMQ that supports blocking read/write
- std::unique_ptr<FilterMQ> tmpFilterMQ =
- std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(bufferSize, true));
- if (!tmpFilterMQ->isValid()) {
- ALOGW("Failed to create FMQ of filter with id: %d", filterId);
- return false;
- }
-
- mFilterMQs.resize(filterId + 1);
- mFilterMQs[filterId] = std::move(tmpFilterMQ);
-
- EventFlag* mFilterEventFlag;
- if (EventFlag::createEventFlag(mFilterMQs[filterId]->getEventFlagWord(), &mFilterEventFlag) !=
- OK) {
- return false;
- }
- mFilterEventFlags.resize(filterId + 1);
- mFilterEventFlags[filterId] = mFilterEventFlag;
- mFilterWriteCount.resize(filterId + 1);
- mFilterWriteCount[filterId] = 0;
- mThreadRunning.resize(filterId + 1);
-
- return true;
-}
-
Return<Result> Demux::setFrontendDataSource(uint32_t frontendId) {
ALOGV("%s", __FUNCTION__);
- mSourceFrontendId = frontendId;
+ if (mTunerService == nullptr) {
+ return Result::NOT_INITIALIZED;
+ }
- return Result::SUCCESS;
+ mFrontend = mTunerService->getFrontendById(frontendId);
+
+ if (mFrontend == nullptr) {
+ return Result::INVALID_STATE;
+ }
+
+ mFrontendSourceFile = mFrontend->getSourceFile();
+
+ mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
+ return startBroadcastInputLoop();
}
Return<void> Demux::addFilter(DemuxFilterType type, uint32_t bufferSize,
const sp<IDemuxCallback>& cb, addFilter_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
- uint32_t filterId = mLastUsedFilterId + 1;
- mLastUsedFilterId += 1;
+ uint32_t filterId;
+
+ if (!mUnusedFilterIds.empty()) {
+ filterId = *mUnusedFilterIds.begin();
+
+ mUnusedFilterIds.erase(filterId);
+ } else {
+ filterId = ++mLastUsedFilterId;
+
+ mDemuxCallbacks.resize(filterId + 1);
+ mFilterMQs.resize(filterId + 1);
+ mFilterEvents.resize(filterId + 1);
+ mFilterEventFlags.resize(filterId + 1);
+ mFilterThreadRunning.resize(filterId + 1);
+ mFilterThreads.resize(filterId + 1);
+ mFilterPids.resize(filterId + 1);
+ mFilterOutputs.resize(filterId + 1);
+ }
+
+ mUsedFilterIds.insert(filterId);
if ((type != DemuxFilterType::PCR || type != DemuxFilterType::TS) && cb == nullptr) {
ALOGW("callback can't be null");
_hidl_cb(Result::INVALID_ARGUMENT, filterId);
return Void();
}
+
// Add callback
- mDemuxCallbacks.resize(filterId + 1);
mDemuxCallbacks[filterId] = cb;
- // Mapping from the filter ID to the filter type
- mFilterTypes.resize(filterId + 1);
- mFilterTypes[filterId] = type;
+ // Mapping from the filter ID to the filter event
+ DemuxFilterEvent event{
+ .filterId = filterId,
+ .filterType = type,
+ };
+ mFilterEvents[filterId] = event;
- if (!createAndSaveMQ(bufferSize, filterId)) {
+ if (!createFilterMQ(bufferSize, filterId)) {
_hidl_cb(Result::UNKNOWN_ERROR, -1);
return Void();
}
@@ -141,8 +146,8 @@
Return<void> Demux::getFilterQueueDesc(uint32_t filterId, getFilterQueueDesc_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
- if (filterId < 0 || filterId > mLastUsedFilterId) {
- ALOGW("No filter with id: %d exists", filterId);
+ if (mUsedFilterIds.find(filterId) == mUsedFilterIds.end()) {
+ ALOGW("No filter with id: %d exists to get desc", filterId);
_hidl_cb(Result::INVALID_ARGUMENT, FilterMQ::Descriptor());
return Void();
}
@@ -151,58 +156,58 @@
return Void();
}
-Return<Result> Demux::configureFilter(uint32_t /* filterId */,
- const DemuxFilterSettings& /* settings */) {
+Return<Result> Demux::configureFilter(uint32_t filterId, const DemuxFilterSettings& settings) {
ALOGV("%s", __FUNCTION__);
+ switch (mFilterEvents[filterId].filterType) {
+ case DemuxFilterType::SECTION:
+ mFilterPids[filterId] = settings.section().tpid;
+ break;
+ case DemuxFilterType::PES:
+ mFilterPids[filterId] = settings.pesData().tpid;
+ break;
+ case DemuxFilterType::TS:
+ mFilterPids[filterId] = settings.ts().tpid;
+ break;
+ case DemuxFilterType::AUDIO:
+ mFilterPids[filterId] = settings.audio().tpid;
+ break;
+ case DemuxFilterType::VIDEO:
+ mFilterPids[filterId] = settings.video().tpid;
+ break;
+ case DemuxFilterType::RECORD:
+ mFilterPids[filterId] = settings.record().tpid;
+ break;
+ case DemuxFilterType::PCR:
+ mFilterPids[filterId] = settings.pcr().tpid;
+ break;
+ default:
+ return Result::UNKNOWN_ERROR;
+ }
return Result::SUCCESS;
}
Return<Result> Demux::startFilter(uint32_t filterId) {
ALOGV("%s", __FUNCTION__);
+ Result result;
- if (filterId < 0 || filterId > mLastUsedFilterId) {
- ALOGW("No filter with id: %d exists", filterId);
+ if (mUsedFilterIds.find(filterId) == mUsedFilterIds.end()) {
+ ALOGW("No filter with id: %d exists to start filter", filterId);
return Result::INVALID_ARGUMENT;
}
- DemuxFilterType filterType = mFilterTypes[filterId];
- Result result;
- DemuxFilterEvent event{
- .filterId = filterId,
- .filterType = filterType,
- };
-
- switch (filterType) {
- case DemuxFilterType::SECTION:
- result = startSectionFilterHandler(event);
- break;
- case DemuxFilterType::PES:
- result = startPesFilterHandler(event);
- break;
- case DemuxFilterType::TS:
- result = startTsFilterHandler();
- return Result::SUCCESS;
- case DemuxFilterType::AUDIO:
- case DemuxFilterType::VIDEO:
- result = startMediaFilterHandler(event);
- break;
- case DemuxFilterType::RECORD:
- result = startRecordFilterHandler(event);
- break;
- case DemuxFilterType::PCR:
- result = startPcrFilterHandler();
- return Result::SUCCESS;
- default:
- return Result::UNKNOWN_ERROR;
- }
+ result = startFilterLoop(filterId);
return result;
}
-Return<Result> Demux::stopFilter(uint32_t /* filterId */) {
+Return<Result> Demux::stopFilter(uint32_t filterId) {
ALOGV("%s", __FUNCTION__);
+ mFilterThreadRunning[filterId] = false;
+
+ std::lock_guard<std::mutex> lock(mFilterThreadLock);
+
return Result::SUCCESS;
}
@@ -212,9 +217,13 @@
return Result::SUCCESS;
}
-Return<Result> Demux::removeFilter(uint32_t /* filterId */) {
+Return<Result> Demux::removeFilter(uint32_t filterId) {
ALOGV("%s", __FUNCTION__);
+ // resetFilterRecords(filterId);
+ mUsedFilterIds.erase(filterId);
+ mUnusedFilterIds.insert(filterId);
+
return Result::SUCCESS;
}
@@ -239,25 +248,330 @@
Return<Result> Demux::close() {
ALOGV("%s", __FUNCTION__);
+ set<uint32_t>::iterator it;
+ mInputThread = 0;
+ mOutputThread = 0;
+ mFilterThreads.clear();
+ mUnusedFilterIds.clear();
+ mUsedFilterIds.clear();
+ mDemuxCallbacks.clear();
+ mFilterMQs.clear();
+ mFilterEvents.clear();
+ mFilterEventFlags.clear();
+ mFilterOutputs.clear();
+ mFilterPids.clear();
+ mLastUsedFilterId = -1;
+
return Result::SUCCESS;
}
-bool Demux::writeSectionsAndCreateEvent(DemuxFilterEvent& event, uint32_t sectionNum) {
- event.events.resize(sectionNum);
- for (int i = 0; i < sectionNum; i++) {
- DemuxFilterSectionEvent secEvent;
- secEvent = {
- // temp dump meta data
- .tableId = 0,
- .version = 1,
- .sectionNum = 1,
- .dataLength = 530,
- };
- event.events[i].section(secEvent);
- if (!writeDataToFilterMQ(fakeDataInputBuffer, event.filterId)) {
- return false;
+Return<Result> Demux::addOutput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ // Create a synchronized FMQ that supports blocking read/write
+ std::unique_ptr<FilterMQ> tmpFilterMQ =
+ std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(bufferSize, true));
+ if (!tmpFilterMQ->isValid()) {
+ ALOGW("Failed to create output FMQ");
+ return Result::UNKNOWN_ERROR;
+ }
+
+ mOutputMQ = std::move(tmpFilterMQ);
+
+ if (EventFlag::createEventFlag(mOutputMQ->getEventFlagWord(), &mOutputEventFlag) != OK) {
+ return Result::UNKNOWN_ERROR;
+ }
+
+ mOutputCallback = cb;
+
+ return Result::SUCCESS;
+}
+
+Return<void> Demux::getOutputQueueDesc(getOutputQueueDesc_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ if (!mOutputMQ) {
+ _hidl_cb(Result::NOT_INITIALIZED, FilterMQ::Descriptor());
+ return Void();
+ }
+
+ _hidl_cb(Result::SUCCESS, *mOutputMQ->getDesc());
+ return Void();
+}
+
+Return<Result> Demux::configureOutput(const DemuxOutputSettings& settings) {
+ ALOGV("%s", __FUNCTION__);
+
+ mOutputConfigured = true;
+ mOutputSettings = settings;
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::attachOutputFilter(uint32_t /*filterId*/) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::detachOutputFilter(uint32_t /* filterId */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::startOutput() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::stopOutput() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::flushOutput() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::removeOutput() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::addInput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ // Create a synchronized FMQ that supports blocking read/write
+ std::unique_ptr<FilterMQ> tmpInputMQ =
+ std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(bufferSize, true));
+ if (!tmpInputMQ->isValid()) {
+ ALOGW("Failed to create input FMQ");
+ return Result::UNKNOWN_ERROR;
+ }
+
+ mInputMQ = std::move(tmpInputMQ);
+
+ if (EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &mInputEventFlag) != OK) {
+ return Result::UNKNOWN_ERROR;
+ }
+
+ mInputCallback = cb;
+
+ return Result::SUCCESS;
+}
+
+Return<void> Demux::getInputQueueDesc(getInputQueueDesc_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ if (!mInputMQ) {
+ _hidl_cb(Result::NOT_INITIALIZED, FilterMQ::Descriptor());
+ return Void();
+ }
+
+ _hidl_cb(Result::SUCCESS, *mInputMQ->getDesc());
+ return Void();
+}
+
+Return<Result> Demux::configureInput(const DemuxInputSettings& settings) {
+ ALOGV("%s", __FUNCTION__);
+
+ mInputConfigured = true;
+ mInputSettings = settings;
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::startInput() {
+ ALOGV("%s", __FUNCTION__);
+
+ if (!mInputCallback) {
+ return Result::NOT_INITIALIZED;
+ }
+
+ if (!mInputConfigured) {
+ return Result::INVALID_STATE;
+ }
+
+ pthread_create(&mInputThread, NULL, __threadLoopInput, this);
+ pthread_setname_np(mInputThread, "demux_input_waiting_loop");
+
+ // TODO start another thread to send filter status callback to the framework
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::stopInput() {
+ ALOGV("%s", __FUNCTION__);
+
+ mInputThreadRunning = false;
+
+ std::lock_guard<std::mutex> lock(mInputThreadLock);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::flushInput() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::removeInput() {
+ ALOGV("%s", __FUNCTION__);
+
+ mInputMQ = nullptr;
+
+ return Result::SUCCESS;
+}
+
+Result Demux::startFilterLoop(uint32_t filterId) {
+ struct ThreadArgs* threadArgs = (struct ThreadArgs*)malloc(sizeof(struct ThreadArgs));
+ threadArgs->user = this;
+ threadArgs->filterId = filterId;
+
+ pthread_t mFilterThread;
+ pthread_create(&mFilterThread, NULL, __threadLoopFilter, (void*)threadArgs);
+ mFilterThreads[filterId] = mFilterThread;
+ pthread_setname_np(mFilterThread, "demux_filter_waiting_loop");
+
+ return Result::SUCCESS;
+}
+
+Result Demux::startSectionFilterHandler(uint32_t filterId) {
+ if (mFilterOutputs[filterId].empty()) {
+ return Result::SUCCESS;
+ }
+ if (!writeSectionsAndCreateEvent(filterId, mFilterOutputs[filterId])) {
+ ALOGD("[Demux] filter %d fails to write into FMQ. Ending thread", filterId);
+ return Result::UNKNOWN_ERROR;
+ }
+
+ mFilterOutputs[filterId].clear();
+
+ return Result::SUCCESS;
+}
+
+Result Demux::startPesFilterHandler(uint32_t filterId) {
+ std::lock_guard<std::mutex> lock(mFilterEventLock);
+ DemuxFilterPesEvent pesEvent;
+ if (mFilterOutputs[filterId].empty()) {
+ return Result::SUCCESS;
+ }
+
+ for (int i = 0; i < mFilterOutputs[filterId].size(); i += 188) {
+ uint8_t pusi = mFilterOutputs[filterId][i + 1] & 0x40;
+ uint8_t adaptFieldControl = (mFilterOutputs[filterId][i + 3] & 0x30) >> 4;
+ ALOGD("[Demux] pusi %d, adaptFieldControl %d", pusi, adaptFieldControl);
+ if (pusi && (adaptFieldControl == 0x01)) {
+ vector<uint8_t>::const_iterator first = mFilterOutputs[filterId].begin() + i + 4;
+ vector<uint8_t>::const_iterator last = mFilterOutputs[filterId].begin() + i + 187;
+ vector<uint8_t> filterOutData(first, last);
+ if (!writeDataToFilterMQ(filterOutData, filterId)) {
+ mFilterOutputs[filterId].clear();
+ return Result::INVALID_STATE;
+ }
+ pesEvent = {
+ // temp dump meta data
+ .streamId = filterOutData[3],
+ .dataLength = static_cast<uint16_t>(filterOutData.size()),
+ };
+ int size = mFilterEvents[filterId].events.size();
+ mFilterEvents[filterId].events.resize(size + 1);
+ mFilterEvents[filterId].events[size].pes(pesEvent);
}
}
+
+ mFilterOutputs[filterId].clear();
+
+ return Result::SUCCESS;
+}
+
+Result Demux::startTsFilterHandler() {
+ // TODO handle starting TS filter
+ return Result::SUCCESS;
+}
+
+Result Demux::startMediaFilterHandler(uint32_t filterId) {
+ DemuxFilterMediaEvent mediaEvent;
+ mediaEvent = {
+ // temp dump meta data
+ .pts = 0,
+ .dataLength = 530,
+ .secureMemory = nullptr,
+ };
+ mFilterEvents[filterId].events.resize(1);
+ mFilterEvents[filterId].events[0].media() = mediaEvent;
+
+ mFilterOutputs[filterId].clear();
+ // TODO handle write FQM for media stream
+ return Result::SUCCESS;
+}
+
+Result Demux::startRecordFilterHandler(uint32_t filterId) {
+ DemuxFilterRecordEvent recordEvent;
+ recordEvent = {
+ // temp dump meta data
+ .tpid = 0,
+ .packetNum = 0,
+ };
+ recordEvent.indexMask.tsIndexMask() = 0x01;
+ mFilterEvents[filterId].events.resize(1);
+ mFilterEvents[filterId].events[0].ts() = recordEvent;
+
+ mFilterOutputs[filterId].clear();
+ return Result::SUCCESS;
+}
+
+Result Demux::startPcrFilterHandler() {
+ // TODO handle starting PCR filter
+ return Result::SUCCESS;
+}
+
+bool Demux::createFilterMQ(uint32_t bufferSize, uint32_t filterId) {
+ ALOGV("%s", __FUNCTION__);
+
+ // Create a synchronized FMQ that supports blocking read/write
+ std::unique_ptr<FilterMQ> tmpFilterMQ =
+ std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(bufferSize, true));
+ if (!tmpFilterMQ->isValid()) {
+ ALOGW("Failed to create FMQ of filter with id: %d", filterId);
+ return false;
+ }
+
+ mFilterMQs[filterId] = std::move(tmpFilterMQ);
+
+ EventFlag* filterEventFlag;
+ if (EventFlag::createEventFlag(mFilterMQs[filterId]->getEventFlagWord(), &filterEventFlag) !=
+ OK) {
+ return false;
+ }
+ mFilterEventFlags[filterId] = filterEventFlag;
+
+ return true;
+}
+
+bool Demux::writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data) {
+ // TODO check how many sections has been read
+ std::lock_guard<std::mutex> lock(mFilterEventLock);
+ if (!writeDataToFilterMQ(data, filterId)) {
+ return false;
+ }
+ int size = mFilterEvents[filterId].events.size();
+ mFilterEvents[filterId].events.resize(size + 1);
+ DemuxFilterSectionEvent secEvent;
+ secEvent = {
+ // temp dump meta data
+ .tableId = 0,
+ .version = 1,
+ .sectionNum = 1,
+ .dataLength = static_cast<uint16_t>(data.size()),
+ };
+ mFilterEvents[filterId].events[size].section(secEvent);
return true;
}
@@ -269,116 +583,106 @@
return false;
}
-Result Demux::startSectionFilterHandler(DemuxFilterEvent event) {
- struct ThreadArgs* threadArgs = (struct ThreadArgs*)malloc(sizeof(struct ThreadArgs));
- threadArgs->user = this;
- threadArgs->event = &event;
+bool Demux::readInputFMQ() {
+ // Read input data from the input FMQ
+ int size = mInputMQ->availableToRead();
+ int inputPacketSize = mInputSettings.packetSize;
+ vector<uint8_t> dataOutputBuffer;
+ dataOutputBuffer.resize(inputPacketSize);
- pthread_create(&mThreadId, NULL, __threadLoop, (void*)threadArgs);
- pthread_setname_np(mThreadId, "demux_filter_waiting_loop");
-
- return Result::SUCCESS;
-}
-
-Result Demux::startPesFilterHandler(DemuxFilterEvent& event) {
- // TODO generate multiple events in one event callback
- DemuxFilterPesEvent pesEvent;
- pesEvent = {
- // temp dump meta data
- .streamId = 0,
- .dataLength = 530,
- };
- event.events.resize(1);
- event.events[0].pes(pesEvent);
- /*pthread_create(&mThreadId, NULL, __threadLoop, this);
- pthread_setname_np(mThreadId, "demux_section_filter_waiting_loop");*/
- if (!writeDataToFilterMQ(fakeDataInputBuffer, event.filterId)) {
- return Result::INVALID_STATE;
+ // Dispatch the packet to the PID matching filter output buffer
+ for (int i = 0; i < size / inputPacketSize; i++) {
+ if (!mInputMQ->read(dataOutputBuffer.data(), inputPacketSize)) {
+ return false;
+ }
+ startTsFilter(dataOutputBuffer);
}
- if (mDemuxCallbacks[event.filterId] == nullptr) {
- return Result::NOT_INITIALIZED;
+ return true;
+}
+
+void Demux::startTsFilter(vector<uint8_t> data) {
+ set<uint32_t>::iterator it;
+ for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
+ uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
+ ALOGW("start ts filter pid: %d", pid);
+ if (pid == mFilterPids[*it]) {
+ mFilterOutputs[*it].insert(mFilterOutputs[*it].end(), data.begin(), data.end());
+ }
+ }
+}
+
+bool Demux::startFilterDispatcher() {
+ Result result;
+ set<uint32_t>::iterator it;
+
+ // Handle the output data per filter type
+ for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
+ switch (mFilterEvents[*it].filterType) {
+ case DemuxFilterType::SECTION:
+ result = startSectionFilterHandler(*it);
+ break;
+ case DemuxFilterType::PES:
+ result = startPesFilterHandler(*it);
+ break;
+ case DemuxFilterType::TS:
+ result = startTsFilterHandler();
+ break;
+ case DemuxFilterType::AUDIO:
+ case DemuxFilterType::VIDEO:
+ result = startMediaFilterHandler(*it);
+ break;
+ case DemuxFilterType::RECORD:
+ result = startRecordFilterHandler(*it);
+ break;
+ case DemuxFilterType::PCR:
+ result = startPcrFilterHandler();
+ break;
+ default:
+ return false;
+ }
}
- mDemuxCallbacks[event.filterId]->onFilterEvent(event);
- return Result::SUCCESS;
+ return result == Result::SUCCESS;
}
-Result Demux::startTsFilterHandler() {
- // TODO handle starting TS filter
- return Result::SUCCESS;
-}
-
-Result Demux::startMediaFilterHandler(DemuxFilterEvent& event) {
- DemuxFilterMediaEvent mediaEvent;
- mediaEvent = {
- // temp dump meta data
- .pts = 0,
- .dataLength = 530,
- .secureMemory = nullptr,
- };
- event.events.resize(1);
- event.events[0].media() = mediaEvent;
- // TODO handle write FQM for media stream
- return Result::SUCCESS;
-}
-
-Result Demux::startRecordFilterHandler(DemuxFilterEvent& event) {
- DemuxFilterRecordEvent recordEvent;
- recordEvent = {
- // temp dump meta data
- .tpid = 0,
- .packetNum = 0,
- };
- recordEvent.indexMask.tsIndexMask() = 0x01;
- event.events.resize(1);
- event.events[0].ts() = recordEvent;
- return Result::SUCCESS;
-}
-
-Result Demux::startPcrFilterHandler() {
- // TODO handle starting PCR filter
- return Result::SUCCESS;
-}
-
-void* Demux::__threadLoop(void* threadArg) {
+void* Demux::__threadLoopFilter(void* threadArg) {
Demux* const self = static_cast<Demux*>(((struct ThreadArgs*)threadArg)->user);
- self->filterThreadLoop(((struct ThreadArgs*)threadArg)->event);
+ self->filterThreadLoop(((struct ThreadArgs*)threadArg)->filterId);
return 0;
}
-void Demux::filterThreadLoop(DemuxFilterEvent* event) {
- uint32_t filterId = event->filterId;
- ALOGD("[Demux] filter %d threadLoop start.", filterId);
- mThreadRunning[filterId] = true;
+void* Demux::__threadLoopInput(void* user) {
+ Demux* const self = static_cast<Demux*>(user);
+ self->inputThreadLoop();
+ return 0;
+}
- while (mThreadRunning[filterId]) {
+void Demux::filterThreadLoop(uint32_t filterId) {
+ ALOGD("[Demux] filter %d threadLoop start.", filterId);
+ std::lock_guard<std::mutex> lock(mFilterThreadLock);
+ mFilterThreadRunning[filterId] = true;
+
+ // For the first time of filter output, implementation needs to send the filter
+ // Event Callback without waiting for the DATA_CONSUMED to init the process.
+ while (mFilterThreadRunning[filterId]) {
+ if (mFilterEvents[filterId].events.size() == 0) {
+ ALOGD("[Demux] wait for filter data output.");
+ usleep(1000 * 1000);
+ continue;
+ }
+ // After successfully write, send a callback and wait for the read to be done
+ mDemuxCallbacks[filterId]->onFilterEvent(mFilterEvents[filterId]);
+ mFilterEvents[filterId].events.resize(0);
+ break;
+ }
+
+ while (mFilterThreadRunning[filterId]) {
uint32_t efState = 0;
// We do not wait for the last round of writen data to be read to finish the thread
// because the VTS can verify the reading itself.
for (int i = 0; i < SECTION_WRITE_COUNT; i++) {
- DemuxFilterEvent filterEvent{
- .filterId = filterId,
- .filterType = event->filterType,
- };
- if (!writeSectionsAndCreateEvent(filterEvent, 2)) {
- ALOGD("[Demux] filter %d fails to write into FMQ. Ending thread", filterId);
- break;
- }
- mFilterWriteCount[filterId]++;
- if (mDemuxCallbacks[filterId] == nullptr) {
- ALOGD("[Demux] filter %d does not hava callback. Ending thread", filterId);
- break;
- }
- // After successfully write, send a callback and wait for the read to be done
- mDemuxCallbacks[filterId]->onFilterEvent(filterEvent);
- // We do not wait for the last read to be done
- // VTS can verify the read result itself.
- if (i == SECTION_WRITE_COUNT - 1) {
- ALOGD("[Demux] filter %d writing done. Ending thread", filterId);
- break;
- }
- while (mThreadRunning[filterId]) {
+ while (mFilterThreadRunning[filterId]) {
status_t status = mFilterEventFlags[filterId]->wait(
static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED), &efState,
WAIT_TIMEOUT, true /* retry on spurious wake */);
@@ -388,15 +692,155 @@
}
break;
}
- }
- mFilterWriteCount[filterId] = 0;
- mThreadRunning[filterId] = false;
+ if (mDemuxCallbacks[filterId] == nullptr) {
+ ALOGD("[Demux] filter %d does not hava callback. Ending thread", filterId);
+ break;
+ }
+
+ while (mFilterThreadRunning[filterId]) {
+ std::lock_guard<std::mutex> lock(mFilterEventLock);
+ if (mFilterEvents[filterId].events.size() == 0) {
+ continue;
+ }
+ // After successfully write, send a callback and wait for the read to be done
+ mDemuxCallbacks[filterId]->onFilterEvent(mFilterEvents[filterId]);
+ mFilterEvents[filterId].events.resize(0);
+ break;
+ }
+ // We do not wait for the last read to be done
+ // VTS can verify the read result itself.
+ if (i == SECTION_WRITE_COUNT - 1) {
+ ALOGD("[Demux] filter %d writing done. Ending thread", filterId);
+ break;
+ }
+ }
+ mFilterThreadRunning[filterId] = false;
}
ALOGD("[Demux] filter thread ended.");
}
+void Demux::inputThreadLoop() {
+ ALOGD("[Demux] input threadLoop start.");
+ std::lock_guard<std::mutex> lock(mInputThreadLock);
+ mInputThreadRunning = true;
+
+ while (mInputThreadRunning) {
+ uint32_t efState = 0;
+ status_t status =
+ mInputEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
+ &efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
+ if (status != OK) {
+ ALOGD("[Demux] wait for data ready on the input FMQ");
+ continue;
+ }
+ // Our current implementation filter the data and write it into the filter FMQ immedaitely
+ // after the DATA_READY from the VTS/framework
+ if (!readInputFMQ() || !startFilterDispatcher()) {
+ ALOGD("[Demux] input data failed to be filtered. Ending thread");
+ break;
+ }
+
+ maySendInputStatusCallback();
+ }
+
+ mInputThreadRunning = false;
+ ALOGD("[Demux] input thread ended.");
+}
+
+void Demux::maySendInputStatusCallback() {
+ std::lock_guard<std::mutex> lock(mInputStatusLock);
+ int availableToRead = mInputMQ->availableToRead();
+ int availableToWrite = mInputMQ->availableToWrite();
+
+ DemuxInputStatus newStatus =
+ checkStatusChange(availableToWrite, availableToRead, mInputSettings.highThreshold,
+ mInputSettings.lowThreshold);
+ if (mIntputStatus != newStatus) {
+ mInputCallback->onInputStatus(newStatus);
+ mIntputStatus = newStatus;
+ }
+}
+
+DemuxInputStatus Demux::checkStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold) {
+ if (availableToWrite == 0) {
+ return DemuxInputStatus::SPACE_FULL;
+ } else if (availableToRead > highThreshold) {
+ return DemuxInputStatus::SPACE_ALMOST_FULL;
+ } else if (availableToRead < lowThreshold) {
+ return DemuxInputStatus::SPACE_ALMOST_EMPTY;
+ } else if (availableToRead == 0) {
+ return DemuxInputStatus::SPACE_EMPTY;
+ }
+ return mIntputStatus;
+}
+
+Result Demux::startBroadcastInputLoop() {
+ pthread_create(&mBroadcastInputThread, NULL, __threadLoopBroadcast, this);
+ pthread_setname_np(mBroadcastInputThread, "broadcast_input_thread");
+
+ return Result::SUCCESS;
+}
+
+void* Demux::__threadLoopBroadcast(void* user) {
+ Demux* const self = static_cast<Demux*>(user);
+ self->broadcastInputThreadLoop();
+ return 0;
+}
+
+void Demux::broadcastInputThreadLoop() {
+ std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
+ mBroadcastInputThreadRunning = true;
+ mKeepFetchingDataFromFrontend = true;
+
+ // open the stream and get its length
+ std::ifstream inputData(mFrontendSourceFile, std::ifstream::binary);
+ // TODO take the packet size from the frontend setting
+ int packetSize = 188;
+ int writePacketAmount = 6;
+ char* buffer = new char[packetSize];
+ ALOGW("[Demux] broadcast input thread loop start %s", mFrontendSourceFile.c_str());
+ if (!inputData.is_open()) {
+ mBroadcastInputThreadRunning = false;
+ ALOGW("[Demux] Error %s", strerror(errno));
+ }
+
+ while (mBroadcastInputThreadRunning) {
+ // move the stream pointer for packet size * 6 every read until the end
+ while (mKeepFetchingDataFromFrontend) {
+ for (int i = 0; i < writePacketAmount; i++) {
+ inputData.read(buffer, packetSize);
+ if (!inputData) {
+ mBroadcastInputThreadRunning = false;
+ break;
+ }
+ // filter and dispatch filter output
+ vector<uint8_t> byteBuffer;
+ byteBuffer.resize(sizeof(buffer));
+ for (int index = 0; index < byteBuffer.size(); index++) {
+ byteBuffer[index] = static_cast<uint8_t>(buffer[index]);
+ }
+ startTsFilter(byteBuffer);
+ inputData.seekg(packetSize, inputData.cur);
+ }
+ startFilterDispatcher();
+ sleep(1);
+ }
+ }
+
+ ALOGW("[Demux] Broadcast Input thread end.");
+ delete[] buffer;
+ inputData.close();
+}
+
+void Demux::stopBroadcastInput() {
+ mKeepFetchingDataFromFrontend = false;
+ mBroadcastInputThreadRunning = false;
+ std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 8b00266..e4a4e2b 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -19,6 +19,9 @@
#include <android/hardware/tv/tuner/1.0/IDemux.h>
#include <fmq/MessageQueue.h>
+#include <set>
+#include "Frontend.h"
+#include "Tuner.h"
using namespace std;
@@ -39,9 +42,14 @@
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+class Tuner;
+class Frontend;
+
class Demux : public IDemux {
public:
- Demux(uint32_t demuxId);
+ Demux(uint32_t demuxId, sp<Tuner> tuner);
+
+ ~Demux();
virtual Return<Result> setFrontendDataSource(uint32_t frontendId) override;
@@ -68,8 +76,69 @@
virtual Return<void> getAvSyncTime(AvSyncHwId avSyncHwId, getAvSyncTime_cb _hidl_cb) override;
+ virtual Return<Result> addInput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) override;
+
+ virtual Return<void> getInputQueueDesc(getInputQueueDesc_cb _hidl_cb) override;
+
+ virtual Return<Result> configureInput(const DemuxInputSettings& settings) override;
+
+ virtual Return<Result> startInput() override;
+
+ virtual Return<Result> stopInput() override;
+
+ virtual Return<Result> flushInput() override;
+
+ virtual Return<Result> removeInput() override;
+
+ virtual Return<Result> addOutput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) override;
+
+ virtual Return<void> getOutputQueueDesc(getOutputQueueDesc_cb _hidl_cb) override;
+
+ virtual Return<Result> configureOutput(const DemuxOutputSettings& settings) override;
+
+ virtual Return<Result> attachOutputFilter(uint32_t filterId) override;
+
+ virtual Return<Result> detachOutputFilter(uint32_t filterId) override;
+
+ virtual Return<Result> startOutput() override;
+
+ virtual Return<Result> stopOutput() override;
+
+ virtual Return<Result> flushOutput() override;
+
+ virtual Return<Result> removeOutput() override;
+
+ // Functions interacts with Tuner Service
+ void stopBroadcastInput();
+
private:
- virtual ~Demux();
+ // Tuner service
+ sp<Tuner> mTunerService;
+
+ // Frontend source
+ sp<Frontend> mFrontend;
+ string mFrontendSourceFile;
+
+ // A struct that passes the arguments to a newly created filter thread
+ struct ThreadArgs {
+ Demux* user;
+ uint32_t filterId;
+ };
+
+ /**
+ * Filter handlers to handle the data filtering.
+ * They are also responsible to write the filtered output into the filter FMQ
+ * and update the filterEvent bound with the same filterId.
+ */
+ Result startSectionFilterHandler(uint32_t filterId);
+ Result startPesFilterHandler(uint32_t filterId);
+ Result startTsFilterHandler();
+ Result startMediaFilterHandler(uint32_t filterId);
+ Result startRecordFilterHandler(uint32_t filterId);
+ Result startPcrFilterHandler();
+ Result startFilterLoop(uint32_t filterId);
+ Result startBroadcastInputLoop();
+
/**
* To create a FilterMQ with the the next available Filter ID.
* Creating Event Flag at the same time.
@@ -77,60 +146,107 @@
*
* Return false is any of the above processes fails.
*/
- bool createAndSaveMQ(uint32_t bufferSize, uint32_t filterId);
+ bool createFilterMQ(uint32_t bufferSize, uint32_t filterId);
+ bool createMQ(FilterMQ* queue, EventFlag* eventFlag, uint32_t bufferSize);
void deleteEventFlag();
bool writeDataToFilterMQ(const std::vector<uint8_t>& data, uint32_t filterId);
- Result startSectionFilterHandler(DemuxFilterEvent event);
- Result startPesFilterHandler(DemuxFilterEvent& event);
- Result startTsFilterHandler();
- Result startMediaFilterHandler(DemuxFilterEvent& event);
- Result startRecordFilterHandler(DemuxFilterEvent& event);
- Result startPcrFilterHandler();
- bool writeSectionsAndCreateEvent(DemuxFilterEvent& event, uint32_t sectionNum);
- void filterThreadLoop(DemuxFilterEvent* event);
- static void* __threadLoop(void* data);
+ bool readDataFromMQ();
+ bool writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data);
+ void maySendInputStatusCallback();
+ DemuxInputStatus checkStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold);
+ /**
+ * A dispatcher to read and dispatch input data to all the started filters.
+ * Each filter handler handles the data filtering/output writing/filterEvent updating.
+ */
+ bool readInputFMQ();
+ void startTsFilter(vector<uint8_t> data);
+ bool startFilterDispatcher();
+ static void* __threadLoopFilter(void* data);
+ static void* __threadLoopInput(void* user);
+ static void* __threadLoopBroadcast(void* user);
+ void filterThreadLoop(uint32_t filterId);
+ void inputThreadLoop();
+ void broadcastInputThreadLoop();
uint32_t mDemuxId;
- uint32_t mSourceFrontendId;
/**
- * Record the last used filer id. Initial value is -1.
+ * Record the last used filter id. Initial value is -1.
* Filter Id starts with 0.
*/
uint32_t mLastUsedFilterId = -1;
/**
+ * Record all the used filter Ids.
+ * Any removed filter id should be removed from this set.
+ */
+ set<uint32_t> mUsedFilterIds;
+ /**
+ * Record all the unused filter Ids within mLastUsedFilterId.
+ * Removed filter Id should be added into this set.
+ * When this set is not empty, ids here should be allocated first
+ * and added into usedFilterIds.
+ */
+ set<uint32_t> mUnusedFilterIds;
+ /**
* A list of created FilterMQ ptrs.
* The array number is the filter ID.
*/
+ vector<uint16_t> mFilterPids;
+ vector<vector<uint8_t>> mFilterOutputs;
vector<unique_ptr<FilterMQ>> mFilterMQs;
- vector<DemuxFilterType> mFilterTypes;
vector<EventFlag*> mFilterEventFlags;
+ vector<DemuxFilterEvent> mFilterEvents;
+ unique_ptr<FilterMQ> mInputMQ;
+ unique_ptr<FilterMQ> mOutputMQ;
+ EventFlag* mInputEventFlag;
+ EventFlag* mOutputEventFlag;
/**
* Demux callbacks used on filter events or IO buffer status
*/
vector<sp<IDemuxCallback>> mDemuxCallbacks;
- /**
- * How many times a specific filter has written since started
- */
- vector<uint16_t> mFilterWriteCount;
- pthread_t mThreadId = 0;
+ sp<IDemuxCallback> mInputCallback;
+ sp<IDemuxCallback> mOutputCallback;
+ bool mInputConfigured = false;
+ bool mOutputConfigured = false;
+ DemuxInputSettings mInputSettings;
+ DemuxOutputSettings mOutputSettings;
+
+ // Thread handlers
+ pthread_t mInputThread;
+ pthread_t mOutputThread;
+ pthread_t mBroadcastInputThread;
+ vector<pthread_t> mFilterThreads;
+
+ // FMQ status local records
+ DemuxInputStatus mIntputStatus;
/**
* If a specific filter's writing loop is still running
*/
- vector<bool> mThreadRunning;
+ vector<bool> mFilterThreadRunning;
+ bool mInputThreadRunning;
+ bool mBroadcastInputThreadRunning;
+ bool mKeepFetchingDataFromFrontend;
/**
* Lock to protect writes to the FMQs
*/
std::mutex mWriteLock;
/**
+ * Lock to protect writes to the filter event
+ */
+ // TODO make each filter separate event lock
+ std::mutex mFilterEventLock;
+ /**
+ * Lock to protect writes to the input status
+ */
+ std::mutex mInputStatusLock;
+ std::mutex mBroadcastInputThreadLock;
+ std::mutex mFilterThreadLock;
+ std::mutex mInputThreadLock;
+ /**
* How many times a filter should write
* TODO make this dynamic/random/can take as a parameter
*/
const uint16_t SECTION_WRITE_COUNT = 10;
- // A struct that passes the arguments to a newly created filter thread
- struct ThreadArgs {
- Demux* user;
- DemuxFilterEvent* event;
- };
};
} // namespace implementation
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index 3dcc2b1..1e07edd 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -27,14 +27,10 @@
namespace V1_0 {
namespace implementation {
-Frontend::Frontend() {
- // Init callback to nullptr
- mCallback = nullptr;
-}
-
-Frontend::Frontend(FrontendType type, FrontendId id) {
+Frontend::Frontend(FrontendType type, FrontendId id, sp<Tuner> tuner) {
mType = type;
mId = id;
+ mTunerService = tuner;
// Init callback to nullptr
mCallback = nullptr;
}
@@ -67,13 +63,52 @@
return Result::INVALID_STATE;
}
- mCallback->onEvent(FrontendEventType::NO_SIGNAL);
+ // TODO dynamically allocate file to the source file
+ mSourceStreamFile = FRONTEND_STREAM_FILE;
+
+ mCallback->onEvent(FrontendEventType::LOCKED);
return Result::SUCCESS;
}
Return<Result> Frontend::stopTune() {
ALOGV("%s", __FUNCTION__);
+ mTunerService->frontendStopTune(mId);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Frontend::scan(const FrontendSettings& /* settings */, FrontendScanType /* type */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Frontend::stopScan() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<void> Frontend::getStatus(const hidl_vec<FrontendStatusType>& /* statusTypes */,
+ getStatus_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ vector<FrontendStatus> statuses;
+ _hidl_cb(Result::SUCCESS, statuses);
+
+ return Void();
+}
+
+Return<Result> Frontend::setLna(bool /* bEnable */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Frontend::setLnb(uint32_t /* lnb */) {
+ ALOGV("%s", __FUNCTION__);
+
return Result::SUCCESS;
}
@@ -85,6 +120,10 @@
return mId;
}
+string Frontend::getSourceFile() {
+ return mSourceStreamFile;
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h
index f77a0d8..07fa7b9 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -18,7 +18,9 @@
#define ANDROID_HARDWARE_TV_TUNER_V1_0_FRONTEND_H_
#include <android/hardware/tv/tuner/1.0/IFrontend.h>
-#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <fstream>
+#include <iostream>
+#include "Tuner.h"
using namespace std;
@@ -35,10 +37,11 @@
using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
using ::android::hardware::tv::tuner::V1_0::Result;
+class Tuner;
+
class Frontend : public IFrontend {
public:
- Frontend();
- Frontend(FrontendType type, FrontendId id);
+ Frontend(FrontendType type, FrontendId id, sp<Tuner> tuner);
virtual Return<Result> close() override;
@@ -48,15 +51,33 @@
virtual Return<Result> stopTune() override;
+ virtual Return<Result> scan(const FrontendSettings& settings, FrontendScanType type) override;
+
+ virtual Return<Result> stopScan() override;
+
+ virtual Return<void> getStatus(const hidl_vec<FrontendStatusType>& statusTypes,
+ getStatus_cb _hidl_cb) override;
+
+ virtual Return<Result> setLna(bool bEnable) override;
+
+ virtual Return<Result> setLnb(uint32_t lnb) override;
+
FrontendType getFrontendType();
FrontendId getFrontendId();
+ string getSourceFile();
+
private:
virtual ~Frontend();
sp<IFrontendCallback> mCallback;
+ sp<Tuner> mTunerService;
FrontendType mType = FrontendType::UNDEFINED;
FrontendId mId = 0;
+
+ const string FRONTEND_STREAM_FILE = "/vendor/etc/test1.ts";
+ string mSourceStreamFile;
+ std::ifstream mFrontendData;
};
} // namespace implementation
diff --git a/tv/tuner/1.0/default/Lnb.cpp b/tv/tuner/1.0/default/Lnb.cpp
new file mode 100644
index 0000000..1446f7f
--- /dev/null
+++ b/tv/tuner/1.0/default/Lnb.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.tuner@1.0-Lnb"
+
+#include "Lnb.h"
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+Lnb::Lnb() {}
+
+Lnb::~Lnb() {}
+
+Return<Result> Lnb::setVoltage(FrontendLnbVoltage /* voltage */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Lnb::setTone(FrontendLnbTone /* tone */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Lnb::setSatellitePosition(FrontendLnbPosition /* position */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Lnb::sendDiseqcMessage(const hidl_vec<uint8_t>& /* diseqcMessage */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Lnb::close() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/Lnb.h b/tv/tuner/1.0/default/Lnb.h
new file mode 100644
index 0000000..4c251f7
--- /dev/null
+++ b/tv/tuner/1.0/default/Lnb.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_LNB_H_
+#define ANDROID_HARDWARE_TV_TUNER_V1_0_LNB_H_
+
+#include <android/hardware/tv/tuner/1.0/ILnb.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::tv::tuner::V1_0::FrontendLnbPosition;
+using ::android::hardware::tv::tuner::V1_0::FrontendLnbTone;
+using ::android::hardware::tv::tuner::V1_0::FrontendLnbVoltage;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+class Lnb : public ILnb {
+ public:
+ Lnb();
+
+ virtual Return<Result> setVoltage(FrontendLnbVoltage voltage) override;
+
+ virtual Return<Result> setTone(FrontendLnbTone tone) override;
+
+ virtual Return<Result> setSatellitePosition(FrontendLnbPosition position) override;
+
+ virtual Return<Result> sendDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override;
+
+ virtual Return<Result> close() override;
+
+ private:
+ virtual ~Lnb();
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_LNB_H_
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 68b3436..f86b28d 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -22,6 +22,7 @@
#include "Demux.h"
#include "Descrambler.h"
#include "Frontend.h"
+#include "Lnb.h"
namespace android {
namespace hardware {
@@ -37,14 +38,14 @@
// Array index matches their FrontendId in the default impl
mFrontendSize = 8;
mFrontends.resize(mFrontendSize);
- mFrontends[0] = new Frontend();
- mFrontends[1] = new Frontend(FrontendType::ATSC, 1);
- mFrontends[2] = new Frontend(FrontendType::DVBC, 2);
- mFrontends[3] = new Frontend(FrontendType::DVBS, 3);
- mFrontends[4] = new Frontend(FrontendType::DVBT, 4);
- mFrontends[5] = new Frontend(FrontendType::ISDBT, 5);
- mFrontends[6] = new Frontend(FrontendType::ANALOG, 6);
- mFrontends[7] = new Frontend(FrontendType::ATSC, 7);
+ mFrontends[0] = new Frontend(FrontendType::DVBT, 0, this);
+ mFrontends[1] = new Frontend(FrontendType::ATSC, 1, this);
+ mFrontends[2] = new Frontend(FrontendType::DVBC, 2, this);
+ mFrontends[3] = new Frontend(FrontendType::DVBS, 3, this);
+ mFrontends[4] = new Frontend(FrontendType::DVBT, 4, this);
+ mFrontends[5] = new Frontend(FrontendType::ISDBT, 5, this);
+ mFrontends[6] = new Frontend(FrontendType::ANALOG, 6, this);
+ mFrontends[7] = new Frontend(FrontendType::ATSC, 7, this);
}
Tuner::~Tuner() {}
@@ -80,12 +81,22 @@
DemuxId demuxId = mLastUsedId + 1;
mLastUsedId += 1;
- sp<IDemux> demux = new Demux(demuxId);
+ sp<Demux> demux = new Demux(demuxId, this);
+ mDemuxes[demuxId] = demux;
_hidl_cb(Result::SUCCESS, demuxId, demux);
return Void();
}
+Return<void> Tuner::getDemuxCaps(getDemuxCaps_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ DemuxCapabilities caps;
+
+ _hidl_cb(Result::SUCCESS, caps);
+ return Void();
+}
+
Return<void> Tuner::openDescrambler(openDescrambler_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
@@ -95,6 +106,52 @@
return Void();
}
+Return<void> Tuner::getFrontendInfo(FrontendId /* frontendId */, getFrontendInfo_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ FrontendInfo info;
+
+ _hidl_cb(Result::SUCCESS, info);
+ return Void();
+}
+
+Return<void> Tuner::getLnbIds(getLnbIds_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ vector<LnbId> lnbIds;
+
+ _hidl_cb(Result::SUCCESS, lnbIds);
+ return Void();
+}
+
+Return<void> Tuner::openLnbById(LnbId /* lnbId */, openLnbById_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ sp<ILnb> lnb = new Lnb();
+
+ _hidl_cb(Result::SUCCESS, lnb);
+ return Void();
+}
+
+sp<Frontend> Tuner::getFrontendById(uint32_t frontendId) {
+ ALOGV("%s", __FUNCTION__);
+
+ return mFrontends[frontendId];
+}
+
+void Tuner::setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId) {
+ mFrontendToDemux[frontendId] = demuxId;
+}
+
+void Tuner::frontendStopTune(uint32_t frontendId) {
+ map<uint32_t, uint32_t>::iterator it = mFrontendToDemux.find(frontendId);
+ uint32_t demuxId;
+ if (it != mFrontendToDemux.end()) {
+ demuxId = it->second;
+ mDemuxes[demuxId]->stopBroadcastInput();
+ }
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Tuner.h b/tv/tuner/1.0/default/Tuner.h
index 12e9594..96da257 100644
--- a/tv/tuner/1.0/default/Tuner.h
+++ b/tv/tuner/1.0/default/Tuner.h
@@ -18,6 +18,8 @@
#define ANDROID_HARDWARE_TV_TUNER_V1_0_TUNER_H_
#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <map>
+#include "Demux.h"
#include "Frontend.h"
using namespace std;
@@ -29,6 +31,9 @@
namespace V1_0 {
namespace implementation {
+class Frontend;
+class Demux;
+
class Tuner : public ITuner {
public:
Tuner();
@@ -39,12 +44,29 @@
virtual Return<void> openDemux(openDemux_cb _hidl_cb) override;
+ virtual Return<void> getDemuxCaps(getDemuxCaps_cb _hidl_cb) override;
+
virtual Return<void> openDescrambler(openDescrambler_cb _hidl_cb) override;
+ virtual Return<void> getFrontendInfo(FrontendId frontendId,
+ getFrontendInfo_cb _hidl_cb) override;
+
+ virtual Return<void> getLnbIds(getLnbIds_cb _hidl_cb) override;
+
+ virtual Return<void> openLnbById(LnbId lnbId, openLnbById_cb _hidl_cb) override;
+
+ sp<Frontend> getFrontendById(uint32_t frontendId);
+
+ void setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId);
+
+ void frontendStopTune(uint32_t frontendId);
+
private:
virtual ~Tuner();
// Static mFrontends array to maintain local frontends information
vector<sp<Frontend>> mFrontends;
+ std::map<uint32_t, uint32_t> mFrontendToDemux;
+ std::map<uint32_t, sp<Demux>> mDemuxes;
// To maintain how many Frontends we have
int mFrontendSize;
// The last used demux id. Initial value is -1.
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
index 4522db2..897818b 100644
--- a/tv/tuner/1.0/types.hal
+++ b/tv/tuner/1.0/types.hal
@@ -41,42 +41,121 @@
enum FrontendType : uint32_t {
UNDEFINED = 0,
ANALOG,
+ /* Advanced Television Systems Committee (ATSC) Standard A/72. */
ATSC,
+ /* Advanced Television Systems Committee (ATSC 3.0) Standard A/300. */
+ ATSC3,
+ /**
+ * Digital Video Broadcasting - Cable
+ * DVB Cable Frontend Standard ETSI EN 300 468 V1.15.1.
+ */
DVBC,
+ /**
+ * Digital Video Broadcasting - Satellite
+ * DVB Satellite Frontend Standard ETSI EN 300 468 V1.15.1 and
+ * ETSI EN 302 307-2 V1.1.1.
+ */
DVBS,
+ /**
+ * Digital Video Broadcasting - Terrestrial
+ * DVB Terresttrial Frontend Standard ETSI EN 300 468 V1.15.1 and
+ * ETSI EN 302 755 V1.4.1.
+ */
DVBT,
+ /* Integrated Services Digital Broadcasting-Satellite (ISDB-S)
+ * ARIB STD-B20 is technical document of ISDB-S.
+ */
+ ISDBS,
+ /* Integrated Services Digital Broadcasting-Satellite (ISDB-S)
+ * ARIB STD-B44 is technical document of ISDB-S3.
+ */
+ ISDBS3,
+ /* Integrated Services Digital Broadcasting-Terrestrial (ISDB-T or SBTVD)
+ * ABNT NBR 15603 is technical document of ISDB-T.
+ */
ISDBT,
};
/**
* Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
- * It's a 4-bit field specifying the inner FEC scheme used according to the
- * table 35 in the spec.
+ * and ETSI EN 302 307-2 V1.1.1.
*/
@export
-enum FrontendInnerFec : uint32_t {
+enum FrontendInnerFec : uint64_t {
/* Not defined */
FEC_UNDEFINED = 0,
- /* 1/2 conv. code rate */
- FEC_1_2 = 1 << 0,
- /* 2/3 conv. code rate */
- FEC_2_3 = 1 << 1,
- /* 3/4 conv. code rate */
- FEC_3_4 = 1 << 2,
- /* 5/6 conv. code rate */
- FEC_5_6 = 1 << 3,
- /* 7/8 conv. code rate */
- FEC_7_8 = 1 << 4,
- /* 8/9 conv. code rate */
- FEC_8_9 = 1 << 5,
- /* 3/5 conv. code rate */
- FEC_3_5 = 1 << 6,
- /* 4/5 conv. code rate */
- FEC_4_5 = 1 << 7,
- /* 9/10 conv. code rate */
- FEC_9_10 = 1 << 8,
/* hardware is able to detect and set FEC automatically */
- FEC_AUTO = 1 << 9,
+ AUTO = 1 << 0,
+ /* 1/2 conv. code rate */
+ FEC_1_2 = 1 << 1,
+ /* 1/3 conv. code rate */
+ FEC_1_3 = 1 << 2,
+ /* 1/4 conv. code rate */
+ FEC_1_4 = 1 << 3,
+ /* 1/5 conv. code rate */
+ FEC_1_5 = 1 << 4,
+ /* 2/3 conv. code rate */
+ FEC_2_3 = 1 << 5,
+ /* 2/5 conv. code rate */
+ FEC_2_5 = 1 << 6,
+ /* 2/9 conv. code rate */
+ FEC_2_9 = 1 << 7,
+ /* 3/4 conv. code rate */
+ FEC_3_4 = 1 << 8,
+ /* 3/5 conv. code rate */
+ FEC_3_5 = 1 << 9,
+ /* 4/5 conv. code rate */
+ FEC_4_5 = 1 << 10,
+ /* 4/15 conv. code rate */
+ FEC_4_15 = 1 << 11,
+ /* 5/6 conv. code rate */
+ FEC_5_6 = 1 << 12,
+ /* 5/9 conv. code rate */
+ FEC_5_9 = 1 << 13,
+ /* 6/7 conv. code rate */
+ FEC_6_7 = 1 << 14,
+ /* 7/8 conv. code rate */
+ FEC_7_8 = 1 << 15,
+ /* 7/9 conv. code rate */
+ FEC_7_9 = 1 << 16,
+ /* 7/15 conv. code rate */
+ FEC_7_15 = 1 << 17,
+ /* 8/9 conv. code rate */
+ FEC_8_9 = 1 << 18,
+ /* 8/15 conv. code rate */
+ FEC_8_15 = 1 << 19,
+ /* 9/10 conv. code rate */
+ FEC_9_10 = 1 << 20,
+ /* 9/20 conv. code rate */
+ FEC_9_20 = 1 << 21,
+ /* 11/15 conv. code rate */
+ FEC_11_15 = 1 << 22,
+ /* 11/20 conv. code rate */
+ FEC_11_20 = 1 << 23,
+ /* 11/45 conv. code rate */
+ FEC_11_45 = 1 << 24,
+ /* 13/18 conv. code rate */
+ FEC_13_18 = 1 << 25,
+ /* 13/45 conv. code rate */
+ FEC_13_45 = 1 << 26,
+ /* 14/45 conv. code rate */
+ FEC_14_45 = 1 << 27,
+ /* 23/36 conv. code rate */
+ FEC_23_36 = 1 << 28,
+ /* 25/36 conv. code rate */
+ FEC_25_36 = 1 << 29,
+ /* 26/45 conv. code rate */
+ FEC_26_45 = 1 << 30,
+ /* 28/45 conv. code rate */
+ FEC_28_45 = 1 << 31,
+ /* 29/45 conv. code rate */
+ FEC_29_45 = 1 << 32,
+ /* 31/45 conv. code rate */
+ FEC_31_45 = 1 << 33,
+ /* 32/45 conv. code rate */
+ FEC_32_45 = 1 << 34,
+ /* 77/90 conv. code rate */
+ FEC_77_90 = 1 << 35,
};
/**
@@ -85,8 +164,10 @@
@export
enum FrontendAtscModulation : uint32_t {
UNDEFINED = 0,
- MOD_8VSB = 1 << 0,
- MOD_16VSB = 1 << 1,
+ /** hardware is able to detect and set modulation automatically */
+ AUTO = 1 << 0,
+ MOD_8VSB = 1 << 2,
+ MOD_16VSB = 1 << 3,
};
/**
@@ -99,21 +180,812 @@
};
/**
+ * Capabilities for ATSC Frontend.
+ */
+struct FrontendAtscCapabilities {
+ /** Modulation capability */
+ bitfield<FrontendAtscModulation> modulationCap;
+};
+
+/**
+ * Modulation Type for ATSC3.
+ */
+@export
+enum FrontendAtsc3Modulation : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set modulation automatically */
+ AUTO = 1 << 0,
+ MOD_QPSK = 1 << 1,
+ MOD_16QAM = 1 << 2,
+ MOD_64QAM = 1 << 3,
+ MOD_256QAM = 1 << 4,
+ MOD_1024QAM = 1 << 5,
+ MOD_4096QAM = 1 << 6,
+};
+
+/**
+ * Bandwidth for ATSC3.
+ */
+@export
+enum FrontendAtsc3Bandwidth : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set bandwidth automatically */
+ AUTO = 1 << 0,
+ BANDWIDTH_6MHZ = 1 << 1,
+ BANDWIDTH_7MHZ = 1 << 2,
+ BANDWIDTH_8MHZ = 1 << 3,
+};
+
+/**
+ * Time Interleave Mode for ATSC3.
+ */
+@export
+enum FrontendAtsc3TimeInterleaveMode : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set TimeInterleaveMode automatically */
+ AUTO = 1 << 0,
+ CTI = 1 << 1,
+ HTI = 1 << 2,
+};
+
+/**
+ * Code Rate for ATSC3.
+ */
+@export
+enum FrontendAtsc3CodeRate : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Coderate automatically */
+ AUTO = 1 << 0,
+ CODERATE_2_15 = 1 << 1,
+ CODERATE_3_15 = 1 << 2,
+ CODERATE_4_15 = 1 << 3,
+ CODERATE_5_15 = 1 << 4,
+ CODERATE_6_15 = 1 << 5,
+ CODERATE_7_15 = 1 << 6,
+ CODERATE_8_15 = 1 << 7,
+ CODERATE_9_15 = 1 << 8,
+ CODERATE_10_15 = 1 << 9,
+ CODERATE_11_15 = 1 << 10,
+ CODERATE_12_15 = 1 << 11,
+ CODERATE_13_15 = 1 << 12,
+};
+
+/**
+ * Forward Error Correction (FEC) for ATSC3.
+ */
+@export
+enum FrontendAtsc3Fec : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set FEC automatically */
+ AUTO = 1 << 0,
+ BCH_LDPC_16K = 1 << 1,
+ BCH_LDPC_64K = 1 << 2,
+ CRC_LDPC_16K = 1 << 3,
+ CRC_LDPC_64K = 1 << 4,
+ LDPC_16K = 1 << 5,
+ LDPC_64K = 1 << 6,
+};
+
+/**
+ * Demodulator Output Format for an ATSC3 Frontend.
+ */
+@export
+enum FrontendAtsc3DemodOutputFormat : uint8_t {
+ /** Dummy. Scan uses this. */
+ UNDEFINED = 0,
+ /** ALP format. Typically used in US region. */
+ ATSC3_LINKLAYER_PACKET = 1 << 0,
+ /** BaseBand packet format. Typically used in Korea region. */
+ BASEBAND_PACKET = 1 << 1,
+};
+
+/**
+ * PLP basis Signal Settings for an ATSC3 Frontend.
+ */
+struct FrontendAtsc3PlpSettings {
+ uint8_t plpId;
+ FrontendAtsc3Modulation modulation;
+ FrontendAtsc3TimeInterleaveMode interleaveMode;
+ FrontendAtsc3CodeRate codeRate;
+ FrontendAtsc3Fec fec;
+};
+
+/**
+ * Signal Settings for an ATSC3 Frontend.
+ */
+struct FrontendAtsc3Settings {
+ /** Signal frequency in Hertz */
+ uint32_t frequency;
+ /** Bandwidth of tuning band. */
+ FrontendAtsc3Bandwidth bandwidth;
+ FrontendAtsc3DemodOutputFormat demodOutputFormat;
+ vec<FrontendAtsc3PlpSettings> plpSettings;
+};
+
+/**
+ * Capabilities for ATSC3 Frontend.
+ */
+struct FrontendAtsc3Capabilities {
+ /** Bandwidth capability */
+ bitfield<FrontendAtsc3Bandwidth> bandwidthCap;
+ /** Modulation capability */
+ bitfield<FrontendAtsc3Modulation> modulationCap;
+ /** TimeInterleaveMode capability */
+ bitfield<FrontendAtsc3TimeInterleaveMode> timeInterleaveModeCap;
+ /** CodeRate capability */
+ bitfield<FrontendAtsc3CodeRate> codeRateCap;
+ /** FEC capability */
+ bitfield<FrontendAtsc3Fec> fecCap;
+ /** Demodulator Output Format capability */
+ bitfield<FrontendAtsc3DemodOutputFormat> demodOutputFormatCap;
+};
+
+/**
+ * Modulation Type for DVBS.
+ */
+@export
+enum FrontendDvbsModulation : int32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Modulation automatically */
+ AUTO = 1 << 0,
+ MOD_QPSK = 1 << 1,
+ MOD_8PSK = 1 << 2,
+ MOD_16QAM = 1 << 3,
+ MOD_16PSK = 1 << 4,
+ MOD_32PSK = 1 << 5,
+ MOD_ACM = 1 << 6,
+ MOD_8APSK = 1 << 7,
+ MOD_16APSK = 1 << 8,
+ MOD_32APSK = 1 << 9,
+ MOD_64APSK = 1 << 10,
+ MOD_128APSK = 1 << 11,
+ MOD_256APSK = 1 << 12,
+ /** Reserved for Proprietary modulation */
+ MOD_RESERVED = 1 << 13,
+};
+
+/**
+ * Roll Off value for DVBS.
+ */
+@export
+enum FrontendDvbsRolloff : uint32_t {
+ UNDEFINED,
+ ROLLOFF_0_35,
+ ROLLOFF_0_25,
+ ROLLOFF_0_20,
+ ROLLOFF_0_15,
+ ROLLOFF_0_10,
+ ROLLOFF_0_5,
+};
+
+/**
+ * Pilot mode for DVBS.
+ */
+@export
+enum FrontendDvbsPilot : uint32_t {
+ UNDEFINED,
+ ON,
+ OFF,
+ AUTO,
+};
+
+/**
+ * Code Rate for DVBS.
+ */
+struct FrontendDvbsCodeRate {
+ FrontendInnerFec fec;
+ bool isLinear;
+ /* true if enable short frame */
+ bool isShortFrames;
+ /* bits number in 1000 symbol. 0 if use the default. */
+ uint32_t bitsPer1000Symbol;
+};
+
+/**
+ * Sub standards in DVBS.
+ */
+@export
+enum FrontendDvbsStandard : uint8_t {
+ AUTO = 1 << 0,
+ S = 1 << 1,
+ S2 = 1 << 2,
+ S2X = 1 << 3,
+};
+
+/**
+ * Signal Settings for an DVBS Frontend.
+ */
+struct FrontendDvbsSettings {
+ /** Signal frequency in Hertz */
+ uint32_t frequency;
+ FrontendDvbsModulation modulation;
+ FrontendDvbsCodeRate coderate;
+ /** Symbols per second */
+ uint32_t symbolRate;
+ FrontendDvbsRolloff rolloff;
+ FrontendDvbsPilot pilot;
+ uint32_t inputStreamId;
+ FrontendDvbsStandard standard;
+};
+
+/**
+ * Capabilities for DVBS Frontend.
+ */
+struct FrontendDvbsCapabilities {
+ bitfield<FrontendDvbsModulation> modulationCap;
+ bitfield<FrontendInnerFec> innerfecCap;
+ bitfield<FrontendDvbsStandard> standard;
+};
+
+/**
+ * Modulation Type for DVBC.
+ */
+@export
+enum FrontendDvbcModulation : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Modulation automatically */
+ AUTO = 1 << 0,
+ MOD_16QAM = 1 << 1,
+ MOD_32QAM = 1 << 2,
+ MOD_64QAM = 1 << 3,
+ MOD_128QAM = 1 << 4,
+ MOD_256QAM = 1 << 5,
+};
+
+/**
+ * Outer Forward Error Correction (FEC) Type for DVBC.
+ */
+@export
+enum FrontendDvbcOuterFec : uint32_t {
+ UNDEFINED = 0,
+ OUTER_FEC_NONE,
+ OUTER_FEC_RS,
+};
+
+/**
+ * Annex Type for DVBC.
+ */
+@export
+enum FrontendDvbcAnnex : uint8_t {
+ UNDEFINED = 0,
+ A = 1 << 0,
+ B = 1 << 1,
+ C = 1 << 2,
+};
+
+/**
+ * Spectral Inversion Type for DVBC.
+ */
+@export
+enum FrontendDvbcSpectralInversion : uint32_t {
+ UNDEFINED,
+ NORMAL,
+ INVERTED,
+};
+
+/**
+ * Signal Settings for an DVBC Frontend.
+ */
+struct FrontendDvbcSettings {
+ /** Signal frequency in Hertz */
+ uint32_t frequency;
+ FrontendDvbcModulation modulation;
+ FrontendInnerFec fec;
+ /** Symbols per second */
+ uint32_t symbolRate;
+ FrontendDvbcOuterFec outerFec;
+ FrontendDvbcAnnex annex;
+ FrontendDvbcSpectralInversion spectralInversion;
+};
+
+/**
+ * Capabilities for DVBC Frontend.
+ */
+struct FrontendDvbcCapabilities {
+ bitfield<FrontendDvbcModulation> modulationCap;
+ bitfield<FrontendInnerFec> fecCap;
+ bitfield<FrontendDvbcAnnex> annexCap;
+};
+
+/**
+ * Bandwidth Type for DVBT.
+ */
+@export
+enum FrontendDvbtBandwidth : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Bandwidth automatically */
+ AUTO = 1 << 0,
+ BANDWIDTH_8MHZ = 1 << 1,
+ BANDWIDTH_7MHZ = 1 << 2,
+ BANDWIDTH_6MHZ = 1 << 3,
+ BANDWIDTH_5MHZ = 1 << 4,
+ BANDWIDTH_1_7MHZ = 1 << 5,
+ BANDWIDTH_10MHZ = 1 << 6,
+};
+
+/**
+ * Constellation Type for DVBT.
+ */
+@export
+enum FrontendDvbtConstellation : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Constellation automatically */
+ AUTO = 1 << 0,
+ CONSTELLATION_QPSK = 1 << 1,
+ CONSTELLATION_16QAM = 1 << 2,
+ CONSTELLATION_64QAM = 1 << 3,
+ CONSTELLATION_256QAM = 1 << 4,
+};
+
+/**
+ * Hierarchy Type for DVBT.
+ */
+@export
+enum FrontendDvbtHierarchy : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Hierarchy automatically */
+ AUTO = 1 << 0,
+ HIERARCHY_NON_NATIVE = 1 << 1,
+ HIERARCHY_1_NATIVE = 1 << 2,
+ HIERARCHY_2_NATIVE = 1 << 3,
+ HIERARCHY_4_NATIVE = 1 << 4,
+ HIERARCHY_NON_INDEPTH = 1 << 5,
+ HIERARCHY_1_INDEPTH = 1 << 6,
+ HIERARCHY_2_INDEPTH = 1 << 7,
+ HIERARCHY_4_INDEPTH = 1 << 8,
+};
+
+/**
+ * Hierarchy Type for DVBT.
+ */
+@export
+enum FrontendDvbtCoderate : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Hierarchy automatically */
+ AUTO = 1 << 0,
+ CODERATE_1_2 = 1 << 1,
+ CODERATE_2_3 = 1 << 2,
+ CODERATE_3_4 = 1 << 3,
+ CODERATE_5_6 = 1 << 4,
+ CODERATE_7_8 = 1 << 5,
+ CODERATE_3_5 = 1 << 6,
+ CODERATE_4_5 = 1 << 7,
+ CODERATE_6_7 = 1 << 8,
+ CODERATE_8_9 = 1 << 9,
+};
+
+/**
+ * Guard Interval Type for DVBT.
+ */
+@export
+enum FrontendDvbtGuardInterval : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Guard Interval automatically */
+ AUTO = 1 << 0,
+ INTERVAL_1_32 = 1 << 1,
+ INTERVAL_1_16 = 1 << 2,
+ INTERVAL_1_8 = 1 << 3,
+ INTERVAL_1_4 = 1 << 4,
+ INTERVAL_1_128 = 1 << 5,
+ INTERVAL_19_128 = 1 << 6,
+ INTERVAL_19_256 = 1 << 7,
+};
+
+/**
+ * Transmission Mode for DVBT.
+ */
+@export
+enum FrontendDvbtTransmissionMode : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Transmission Mode automatically */
+ AUTO = 1 << 0,
+ MODE_2K = 1 << 1,
+ MODE_8K = 1 << 2,
+ MODE_4K = 1 << 3,
+ MODE_1K = 1 << 4,
+ MODE_16K = 1 << 5,
+ MODE_32K = 1 << 6,
+};
+
+/**
+ * Physical Layer Pipe (PLP) Mode for DVBT.
+ */
+enum FrontendDvbtPlpMode : uint32_t {
+ UNDEFINED,
+ AUTO,
+ MANUAL,
+};
+
+/**
+ * Sub standards in DVBT.
+ */
+@export
+enum FrontendDvbtStandard : uint8_t {
+ AUTO = 1 << 0,
+ T = 1 << 1,
+ T2 = 1 << 2,
+};
+
+/**
* Signal Setting for DVBT Frontend.
*/
struct FrontendDvbtSettings {
/** Signal frequencey in Herhz */
uint32_t frequency;
- FrontendAtscModulation modulation;
- FrontendInnerFec fec;
+ FrontendDvbtTransmissionMode transmissionMode;
+ FrontendDvbtBandwidth bandwidth;
+ FrontendDvbtConstellation constellation;
+ FrontendDvbtHierarchy hierarchy;
+ /** Code Rate for High Priority level */
+ FrontendDvbtCoderate hpCoderate;
+ /** Code Rate for Low Priority level */
+ FrontendDvbtCoderate lpCoderate;
+ FrontendDvbtGuardInterval guardInterval;
+ bool isHighPriority;
+ FrontendDvbtStandard standard;
+ bool isMiso;
+ FrontendDvbtPlpMode plpMode;
+ /** Physical Layer Pipe (PLP) Id */
+ uint8_t plpId;
+ /** Group Id for Physical Layer Pipe (PLP) */
+ uint8_t plpGroupId;
};
/**
- * Modulation Type for ATSC.
+ * Capabilities for DVBT Frontend.
+ */
+struct FrontendDvbtCapabilities {
+ bitfield<FrontendDvbtTransmissionMode> transmissionModeCap;
+ bitfield<FrontendDvbtBandwidth> bandwidthCap;
+ bitfield<FrontendDvbtConstellation> constellationCap;
+ bitfield<FrontendDvbtCoderate> coderateCap;
+ bitfield<FrontendDvbtHierarchy> hierarchyCap;
+ bitfield<FrontendDvbtGuardInterval> guardIntervalCap;
+ bool isT2Supported;
+ bool isMisoSupported;
+};
+
+/**
+ * Roll Off Type for ISDBS.
+ */
+@export
+enum FrontendIsdbsRolloff : uint32_t {
+ UNDEFINED,
+ ROLLOFF_0_35,
+};
+
+/**
+ * Modulaltion Type for ISDBS.
+ */
+@export
+enum FrontendIsdbsModulation : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Modulation automatically */
+ AUTO = 1 << 0,
+ MOD_BPSK = 1 << 1,
+ MOD_QPSK = 1 << 2,
+ MOD_TC8PSK = 1 << 3,
+};
+
+/**
+ * Code Rate Type for ISDBS.
+ */
+@export
+enum FrontendIsdbsCoderate : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Code Rate automatically */
+ AUTO = 1 << 0,
+ CODERATE_1_2 = 1 << 1,
+ CODERATE_2_3 = 1 << 2,
+ CODERATE_3_4 = 1 << 3,
+ CODERATE_5_6 = 1 << 4,
+ CODERATE_7_8 = 1 << 5,
+};
+
+/**
+ * Stream Id Type for ISDBS.
+ */
+@export
+enum FrontendIsdbsStreamIdType : uint32_t {
+ STREAM_ID,
+ RELATIVE_STREAM_NUMBER,
+};
+
+/**
+ * Signal Setting for ISDBS Frontend.
+ */
+struct FrontendIsdbsSettings {
+ /** Signal frequency in Hertz */
+ uint32_t frequency;
+ uint16_t streamId;
+ FrontendIsdbsStreamIdType streamIdType;
+ FrontendIsdbsModulation modulation;
+ FrontendIsdbsCoderate coderate;
+ /** Symbols per second */
+ uint32_t symbolRate;
+ FrontendIsdbsRolloff rolloff;
+};
+
+/**
+ * Capabilities for ISDBS Frontend.
+ */
+struct FrontendIsdbsCapabilities {
+ bitfield<FrontendIsdbsModulation> modulationCap;
+ bitfield<FrontendIsdbsCoderate> coderateCap;
+};
+
+/**
+ * Roll of Type for ISDBS3.
+ */
+@export
+enum FrontendIsdbs3Rolloff : uint32_t {
+ UNDEFINED,
+ ROLLOFF_0_03,
+};
+
+/**
+ * Modulaltion Type for ISDBS3.
+ */
+@export
+enum FrontendIsdbs3Modulation : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Modulation automatically */
+ AUTO = 1 << 5,
+ MOD_BPSK = 1 << 1,
+ MOD_QPSK = 1 << 2,
+ MOD_8PSK = 1 << 3,
+ MOD_16APSK = 1 << 4,
+ MOD_32APSK = 1 << 5,
+};
+
+/**
+ * Code Rate Type for ISDBS3.
+ */
+@export
+enum FrontendIsdbs3Coderate : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Code Rate automatically */
+ AUTO = 1 << 0,
+ CODERATE_1_3 = 1 << 1,
+ CODERATE_2_5 = 1 << 2,
+ CODERATE_1_2 = 1 << 3,
+ CODERATE_3_5 = 1 << 4,
+ CODERATE_2_3 = 1 << 5,
+ CODERATE_3_4 = 1 << 6,
+ CODERATE_7_9 = 1 << 7,
+ CODERATE_4_5 = 1 << 8,
+ CODERATE_5_6 = 1 << 9,
+ CODERATE_7_8 = 1 << 10,
+ CODERATE_9_10 = 1 << 11,
+};
+
+/**
+ * Signal Setting for ISDBS3 Frontend.
+ */
+struct FrontendIsdbs3Settings {
+ /** Signal frequency in Hertz */
+ uint32_t frequency;
+ uint16_t streamId;
+ FrontendIsdbsStreamIdType streamIdType;
+ FrontendIsdbs3Modulation modulation;
+ FrontendIsdbs3Coderate coderate;
+ /** Symbols per second */
+ uint32_t symbolRate;
+ FrontendIsdbs3Rolloff rolloff;
+};
+
+/**
+ * Capabilities for ISDBS3 Frontend.
+ */
+struct FrontendIsdbs3Capabilities {
+ bitfield<FrontendIsdbs3Modulation> modulationCap;
+ bitfield<FrontendIsdbs3Coderate> coderateCap;
+};
+
+/**
+ * Mode for ISDBT.
+ */
+@export
+enum FrontendIsdbtMode : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Mode automatically */
+ AUTO = 1 << 0,
+ MODE_1 = 1 << 1,
+ MODE_2 = 1 << 2,
+ MODE_3 = 1 << 3,
+};
+
+/**
+ * Bandwidth for ISDBT.
+ */
+@export
+enum FrontendIsdbtBandwidth : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Bandwidth automatically */
+ AUTO = 1 << 0,
+ BANDWIDTH_8MHZ = 1 << 1,
+ BANDWIDTH_7MHZ = 1 << 2,
+ BANDWIDTH_6MHZ = 1 << 3,
+};
+
+/**
+ * Modulation for ISDBT.
+ */
+@export
+enum FrontendIsdbtModulation : uint32_t {
+ UNDEFINED = 0,
+ /** hardware is able to detect and set Modulation automatically */
+ AUTO = 1 << 0,
+ MOD_DQPSK = 1 << 1,
+ MOD_QPSK = 1 << 2,
+ MOD_16QAM = 1 << 3,
+ MOD_64QAM = 1 << 4,
+};
+
+/** Code Rate for ISDBT. */
+typedef FrontendDvbtCoderate FrontendIsdbtCoderate;
+
+/** Guard Interval for ISDBT. */
+typedef FrontendDvbtGuardInterval FrontendIsdbtGuardInterval;
+
+/**
+ * Signal Setting for ISDBT Frontend.
+ */
+struct FrontendIsdbtSettings {
+ /** Signal frequency in Hertz */
+ uint32_t frequency;
+ FrontendIsdbtModulation modulation;
+ FrontendIsdbtBandwidth bandwidth;
+ FrontendIsdbtMode mode;
+ FrontendIsdbtCoderate coderate;
+ FrontendIsdbtGuardInterval guardInterval;
+ uint32_t serviceAreaId;
+};
+
+/**
+ * Capabilities for ISDBT Frontend.
+ */
+struct FrontendIsdbtCapabilities {
+ bitfield<FrontendIsdbtMode> modeCap;
+ bitfield<FrontendIsdbtBandwidth> bandwidthCap;
+ bitfield<FrontendIsdbtModulation> constellationCap;
+ bitfield<FrontendIsdbtCoderate> coderateCap;
+ bitfield<FrontendIsdbtGuardInterval> guardIntervalCap;
+};
+
+/**
+ * Signal Type for Analog Frontend.
+ */
+@export
+enum FrontendAnalogType : uint32_t {
+ UNDEFINED = 0,
+ PAL = 1 << 0,
+ SECAM = 1 << 1,
+ NTSC = 1 << 2,
+};
+
+/**
+ * Standard Interchange Format (SIF) for Analog Frontend.
+ */
+@export
+enum FrontendAnalogSifStandard : uint32_t {
+ UNDEFINED = 0,
+ BG = 1 << 0,
+ BG_A2 = 1 << 1,
+ BG_NICAM = 1 << 2,
+ I = 1 << 3,
+ DK = 1 << 4,
+ DK1 = 1 << 5,
+ DK2 = 1 << 6,
+ DK3 = 1 << 7,
+ DK_NICAM = 1 << 8,
+ L = 1 << 9,
+ M = 1 << 10,
+ M_BTSC = 1 << 11,
+ M_A2 = 1 << 12,
+ M_EIA_J = 1 << 13,
+ I_NICAM = 1 << 14,
+ L_NICAM = 1 << 15,
+ L_PRIME = 1 << 16,
+};
+
+/**
+ * Signal Setting for Analog Frontend.
+ */
+struct FrontendAnalogSettings {
+ /** Signal frequency in Hertz */
+ uint32_t frequency;
+ FrontendAnalogType type;
+ FrontendAnalogSifStandard sifStandard;
+};
+
+/**
+ * Capabilities for Analog Frontend.
+ */
+struct FrontendAnalogCapabilities {
+ bitfield<FrontendAnalogType> typeCap;
+ bitfield<FrontendAnalogSifStandard> sifStandardCap;
+};
+
+/**
+ * Signal Setting for Frontend.
*/
safe_union FrontendSettings {
+ FrontendAnalogSettings analog;
FrontendAtscSettings atsc;
+ FrontendAtsc3Settings atsc3;
+ FrontendDvbsSettings dvbs;
+ FrontendDvbcSettings dvbc;
FrontendDvbtSettings dvbt;
+ FrontendIsdbsSettings isdbs;
+ FrontendIsdbs3Settings isdbs3;
+ FrontendIsdbtSettings isdbt;
+};
+
+/**
+ * Scan type for Frontend.
+ */
+enum FrontendScanType : uint32_t {
+ SCAN_UNDEFINED = 0,
+ SCAN_AUTO = 1 << 0,
+ SCAN_BLIND = 1 << 1,
+};
+
+/**
+ * Scan Message Type for Frontend.
+ */
+enum FrontendScanMessageType : uint32_t {
+ /** Scan locked the signal. */
+ LOCKED,
+ /** Scan stopped. */
+ END,
+ /** Scan progress report. */
+ PROGRESS_PERCENT,
+ /** Locked frequency report. */
+ FREQUENCY,
+ /** Locked symbol rate. */
+ SYMBOL_RATE,
+ /** Locked Plp Ids for DVBT2 frontend. */
+ PLP_IDS,
+ /** Locked group Ids for DVBT2 frontend. */
+ GROUP_IDS,
+ /** Stream Ids. */
+ INPUT_STREAM_IDS,
+ /** Locked signal stardard. */
+ STANDARD,
+ /** PLP status in a tuned frequency band for ATSC3 frontend. */
+ ATSC3_PLP_INFO,
+};
+
+/**
+ * ATSC3.0 PLP information for scan
+ */
+struct FrontendScanAtsc3PlpInfo {
+ uint8_t plpId;
+ bool bLlsFlag;
+};
+
+/**
+ * Scan Message for Frontend.
+ */
+safe_union FrontendScanMessage {
+ bool islocked;
+ bool isEnd;
+ /** scan progress percent (0..100) */
+ uint8_t progressPercent;
+ /** Signal frequency in Hertz */
+ uint32_t frequency;
+ /** Symbols per second */
+ uint32_t symbolRate;
+ vec<uint8_t> plpIds;
+ vec<uint8_t> groupIds;
+ vec<uint16_t> inputStreamIds;
+ safe_union standard {
+ FrontendDvbsStandard sStd;
+ FrontendDvbtStandard tStd;
+ } std;
+ /** A list of PLP status in a tuned frequency band for ATSC3 frontend. */
+ vec<FrontendScanAtsc3PlpInfo> atsc3PlpInfos;
};
/**
@@ -122,20 +994,245 @@
@export
enum FrontendEventType : uint32_t {
/**
- * If frontend locked the signal which is specified by tune method, HAL sent
+ * If frontend locked the signal which is specified by tune method, HAL sends
* Locked event.
*/
LOCKED,
/**
* If frontend can't locked the signal which is specified by tune method,
- * HAL sent NO_SIGNAL event.
+ * HAL sends NO_SIGNAL event.
*/
NO_SIGNAL,
/**
- * If frontend detect that the locked signal get lost, HAL sent LOST_LOCK
+ * If frontend detect that the locked signal get lost, HAL sends LOST_LOCK
* event.
*/
LOST_LOCK,
+ /**
+ * If frontend detect that incoming Diseqc message is overflow.
+ */
+ DISEQC_RX_OVERFLOW,
+ /**
+ * If frontend detect that outgoing Diseqc message isn't delivered on time.
+ */
+ DISEQC_RX_TIMEOUT,
+ /**
+ * If frontend detect that the incoming Diseqc message has parity error.
+ */
+ DISEQC_RX_PARITY_ERROR,
+ /**
+ * If frontend detect that the LNB is overload.
+ */
+ LNB_OVERLOAD,
+};
+
+/**
+ * Frontend Status Type.
+ */
+@export
+enum FrontendStatusType : uint32_t {
+ /** Lock status for Demod. */
+ DEMOD_LOCK,
+ /** Signal to Noise Ratio. */
+ SNR,
+ /** Bit Error Ratio. */
+ BER,
+ /** Packages Error Ratio. */
+ PER,
+ /** Bit Error Ratio before FEC. */
+ PRE_BER,
+ /*
+ * Signal Quality (0..100). Good data over total data in percent can be
+ * used as a way to present Signal Quality.
+ */
+ SIGNAL_QUALITY,
+ /** Signal Strength. */
+ SIGNAL_STRENGTH,
+ /** Symbol Rate. */
+ SYMBOL_RATE,
+ /** Forward Error Correction Type. */
+ FEC,
+ /** Modulation Type. */
+ MODULATION,
+ /** Spectral Inversion Type. */
+ SPECTRAL,
+ /** LNB Voltage. */
+ LNB_VOLTAGE,
+ /** Physical Layer Pipe ID. */
+ PLP_ID,
+ /** Status for Emergency Warning Broadcasting System. */
+ EWBS,
+ /** Automatic Gain Control. */
+ AGC,
+ /** Low Noise Amplifier. */
+ LNA,
+ /** Lock status for stream. */
+ STREAM_LOCK,
+ /** Error status by layer. */
+ LAYER_ERROR,
+ /** CN value by VBER. */
+ VBER_CN,
+ /** CN value by LBER. */
+ LBER_CN,
+ /** CN value by XER. */
+ XER_CN,
+ /** Moduration Error Ratio. */
+ MER,
+ /** Difference between tuning frequency and actual locked frequency. */
+ FREQ_OFFSET,
+ /* Hierarchy for DVBT. */
+ HIERARCHY,
+ /** Lock status for RF. */
+ RF_LOCK,
+ /** PLP information in a frequency band for ATSC3.0 frontend. */
+ ATSC3_PLP_INFO,
+};
+
+/**
+ * Status for each tuning PLPs
+ */
+struct FrontendStatusAtsc3PlpInfo {
+ /** PLP Id value. */
+ uint8_t plpId;
+ /** Demod Lock/Unlock status of this particular PLP. */
+ bool isLocked;
+ /** Uncorrectable Error Counts (UEC) of this particular PLP since last tune operation. */
+ uint32_t uec;
+};
+
+
+/**
+ * Modulation Type for Frontend's status.
+ */
+safe_union FrontendModulationStatus {
+ FrontendDvbcModulation dvbc;
+ FrontendDvbsModulation dvbs;
+ FrontendIsdbsModulation isdbs;
+ FrontendIsdbs3Modulation isdbs3;
+ FrontendIsdbtModulation isdbt;
+};
+
+/**
+ * The status for Frontend.
+ */
+safe_union FrontendStatus {
+ /** Lock status for Demod in True/False. */
+ bool isDemodLocked;
+ /** SNR value measured by 0.001 dB. */
+ int32_t snr;
+ /** The number of error bit per 1 billion bits. */
+ uint32_t ber;
+ /** The number of error package per 1 billion packages. */
+ uint32_t per;
+ /** The number of error bit per 1 billion bits before FEC. */
+ uint32_t preBer;
+ /** Signal Quality in percent. */
+ uint32_t signalQuality;
+ /** Signal Strength measured by 0.001 dBm. */
+ int32_t signalStrength;
+ /** Symbols per second */
+ uint32_t symbolRate;
+ FrontendInnerFec innerFec;
+ FrontendModulationStatus modulation;
+ FrontendDvbcSpectralInversion inversion;
+ FrontendLnbVoltage lnbVoltage;
+ uint8_t plpId;
+ bool isEWBS;
+ /** AGC value is normalized from 0 to 255. */
+ uint8_t agc;
+ bool isLnaOn;
+ bool isStreamLock;
+ vec<bool> isLayerError;
+ /** CN value by VBER measured by 0.001 dB */
+ int32_t vberCn;
+ /** CN value by LBER measured by 0.001 dB */
+ int32_t lberCn;
+ /** CN value by XER measured by 0.001 dB */
+ int32_t xerCn;
+ /** MER value measured by 0.001 dB */
+ int32_t mer;
+ /** Frequency difference in Hertz. */
+ int32_t freqOffset;
+ FrontendDvbtHierarchy hierarchy;
+ bool isRfLocked;
+ /** A list of PLP status for tuned PLPs for ATSC3 frontend. */
+ vec<FrontendStatusAtsc3PlpInfo> plpInfo;
+};
+
+/**
+ * Information for the Frontend.
+ */
+struct FrontendInfo {
+ FrontendType type;
+ /** Frequency in Hertz */
+ uint32_t minFrequency;
+ /** Frequency in Hertz */
+ uint32_t maxFrequency;
+ /** Minimum symbols per second */
+ uint32_t minSymbolRate;
+ /** Maximum symbols per second */
+ uint32_t maxSymbolRate;
+ /** Range in Hertz */
+ uint32_t acquireRange;
+ /*
+ * Frontends are assigned with the same exclusiveGroupId if they can't
+ * function at same time. For instance, they share same hardware module.
+ */
+ uint32_t exclusiveGroupId;
+ /** A list of supported status types which client can inquiry */
+ vec<FrontendStatusType> statusCaps;
+ safe_union FrontendCapabilities {
+ FrontendAnalogCapabilities analogCaps;
+ FrontendAtscCapabilities atscCaps;
+ FrontendAtsc3Capabilities atsc3Caps;
+ FrontendDvbsCapabilities dvbsCaps;
+ FrontendDvbcCapabilities dvbcCaps;
+ FrontendDvbtCapabilities dvbtCaps;
+ FrontendIsdbsCapabilities isdbsCaps;
+ FrontendIsdbs3Capabilities isdbs3Caps;
+ FrontendIsdbtCapabilities isdbtCaps;
+ } frontendCaps;
+};
+
+/*
+ * Low-Noise Block downconverter (LNB) ID is used to associate with a hardware
+ * LNB module.
+ */
+typedef uint32_t LnbId;
+
+/**
+ * Power Voltage Type for LNB.
+ */
+@export
+enum FrontendLnbVoltage : uint32_t {
+ NONE,
+ VOLTAGE_5V,
+ VOLTAGE_11V,
+ VOLTAGE_12V,
+ VOLTAGE_13V,
+ VOLTAGE_14V,
+ VOLTAGE_15V,
+ VOLTAGE_18V,
+ VOLTAGE_19V,
+};
+
+/**
+ * Tone Type for LNB.
+ */
+@export
+enum FrontendLnbTone : int32_t {
+ NONE,
+ CONTINUOUS,
+};
+
+/**
+ * The Position of LNB.
+ */
+@export
+enum FrontendLnbPosition : int32_t {
+ UNDEFINED,
+ POSITION_A,
+ POSITION_B,
};
/* Demux ID is used to associate with a hardware demux resource. */
@@ -166,7 +1263,7 @@
*/
AUDIO,
/**
- * A filter to filter Vidoe Metadata out from input stream.
+ * A filter to filter Video Metadata out from input stream.
*/
VIDEO,
/**
@@ -248,7 +1345,7 @@
/* Version number for Section Filter */
uint16_t version;
/* true if the filter checks CRC and discards data with wrong CRC */
- bool checkCrc;
+ bool isCheckCrc;
/* true if the filter repeats the data with the same version */
bool isRepeat;
/* true if the filter output raw data */
@@ -265,7 +1362,7 @@
DemuxTpid tpid;
DemuxStreamId streamId;
/* true if the filter output raw data */
- bool bIsRaw;
+ bool isRaw;
};
/**
@@ -283,7 +1380,7 @@
/**
* true if the filter output goes to decoder directly in pass through mode.
*/
- bool bPassthrough;
+ bool isPassthrough;
};
/**
@@ -294,7 +1391,7 @@
/**
* true if the filter output goes to decoder directly in pass through mode.
*/
- bool bPassthrough;
+ bool isPassthrough;
};
/**
@@ -479,3 +1576,132 @@
* framework and apps.
*/
typedef vec<uint8_t> TunerKeyToken;
+
+/**
+ * A data format in demux's output or input according to ISO/IEC 13818-1.
+ */
+@export
+enum DemuxDataFormat : uint32_t {
+ /* Data is Transport Stream. */
+ TS,
+ /* Data is Packetized Elementary Stream. */
+ PES,
+ /* Data is Elementary Stream. */
+ ES,
+ /* Data is TLV (type-length-value) Stream for JP SHV */
+ SHV_TLV,
+};
+
+/**
+ * A status of the demux's output.
+ */
+typedef DemuxFilterStatus DemuxOutputStatus;
+
+/**
+ * The Settings for the demux's output.
+ */
+struct DemuxOutputSettings {
+ /**
+ * Register for interested status events so that the HAL can send these
+ * status events back to client.
+ */
+ bitfield<DemuxOutputStatus> statusMask;
+ /**
+ * Unconsumed data size in bytes in the output. The HAL uses it to trigger
+ * DemuxOutputStatus::LOW_WATER.
+ */
+ uint32_t lowThreshold;
+ /**
+ * Unconsumed data size in bytes in the output. The HAL uses it to trigger
+ * DemuxOutputStatus::High_WATER.
+ */
+ uint32_t highThreshold;
+ /**
+ * The data format in the output.
+ */
+ DemuxDataFormat dataFormat;
+ /**
+ * The packet size in bytes in the output.
+ */
+ uint8_t packetSize;
+};
+
+/**
+ * A status of the demux's input.
+ */
+@export
+enum DemuxInputStatus : uint32_t {
+ /**
+ * The space of the demux's input is empty.
+ */
+ SPACE_EMPTY = 1 << 0,
+ /**
+ * The spece of the demux's input is almost empty.
+ */
+ SPACE_ALMOST_EMPTY = 1 << 1,
+ /**
+ * The space of the demux's input is almost full.
+ */
+ SPACE_ALMOST_FULL = 1 << 2,
+ /**
+ * The space of the demux's input is full.
+ */
+ SPACE_FULL = 1 << 3,
+};
+
+/**
+ * The Settings for the demux's input.
+ */
+@export
+struct DemuxInputSettings {
+ /**
+ * Register for interested status events so that the HAL can send these
+ * status events back to client.
+ */
+ bitfield<DemuxInputStatus> statusMask;
+ /**
+ * Unused space size in bytes in the input. The HAL uses it to trigger
+ * DemuxInputStatus::SPACE_ALMOST_EMPTY.
+ */
+ uint32_t lowThreshold;
+ /**
+ * Unused space size in bytes in the input. The HAL uses it to trigger
+ * DemuxInputStatus::SPACE_ALMOST_FULL.
+ */
+ uint32_t highThreshold;
+ /**
+ * The data format in the input.
+ */
+ DemuxDataFormat dataFormat;
+ /**
+ * The packet size in bytes in the input.
+ */
+ uint8_t packetSize;
+};
+
+/**
+ * Capabilities for Demux.
+ */
+@export
+struct DemuxCapabilities {
+ /* The number of Demux to be supported. */
+ uint32_t numDemux;
+ /* The number of Input to be supported. */
+ uint32_t numInput;
+ /* The number of Output to be supported. */
+ uint32_t numOutput;
+ /* The number of TS Filter to be supported. */
+ uint32_t numTsFilter;
+ /* The number of Section Filter to be supported. */
+ uint32_t numSectionFilter;
+ /* The number of Audio Filter to be supported. */
+ uint32_t numAudioFilter;
+ /* The number of Video Filter to be supported. */
+ uint32_t numVideoFilter;
+ /* The number of PES Filter to be supported. */
+ uint32_t numPesFilter;
+ /* The number of PCR Filter to be supported. */
+ uint32_t numPcrFilter;
+ /* The maximum number of bytes is supported in the mask of Section Filter. */
+ uint32_t numBytesInSectionFilter;
+};
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 66adb2a..7936185 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -34,8 +34,12 @@
#include <hidlmemory/FrameworkUtils.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
+#include <fstream>
+#include <iostream>
+#include <map>
#define WAIT_TIMEOUT 3000000000
+#define WAIT_TIMEOUT_data_ready 3000000000 * 4
using android::Condition;
using android::IMemory;
@@ -53,11 +57,18 @@
using android::hardware::MQDescriptorSync;
using android::hardware::Return;
using android::hardware::Void;
+using android::hardware::tv::tuner::V1_0::DemuxDataFormat;
using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
using android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxInputSettings;
+using android::hardware::tv::tuner::V1_0::DemuxInputStatus;
+using android::hardware::tv::tuner::V1_0::DemuxOutputStatus;
using android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
using android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
using android::hardware::tv::tuner::V1_0::FrontendAtscSettings;
@@ -65,6 +76,8 @@
using android::hardware::tv::tuner::V1_0::FrontendEventType;
using android::hardware::tv::tuner::V1_0::FrontendId;
using android::hardware::tv::tuner::V1_0::FrontendInnerFec;
+using android::hardware::tv::tuner::V1_0::FrontendScanMessage;
+using android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
using android::hardware::tv::tuner::V1_0::FrontendSettings;
using android::hardware::tv::tuner::V1_0::IDemux;
using android::hardware::tv::tuner::V1_0::IDemuxCallback;
@@ -77,9 +90,9 @@
namespace {
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-using FilterMQDesc = MQDescriptorSync<uint8_t>;
+using MQDesc = MQDescriptorSync<uint8_t>;
-const std::vector<uint8_t> goldenDataInputBuffer{
+const std::vector<uint8_t> goldenDataOutputBuffer{
0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
@@ -119,9 +132,17 @@
};
const uint16_t FMQ_SIZE_4K = 0x1000;
-// Equal to SECTION_WRITE_COUNT on the HAL impl side
-// The HAL impl will repeatedly write to the FMQ the count times
-const uint16_t SECTION_READ_COUNT = 10;
+const uint32_t FMQ_SIZE_1M = 0x100000;
+
+struct FilterConf {
+ DemuxFilterType type;
+ DemuxFilterSettings setting;
+};
+
+struct InputConf {
+ string inputDataFile;
+ DemuxInputSettings setting;
+};
class FrontendCallback : public IFrontendCallback {
public:
@@ -141,12 +162,21 @@
return Void();
}
+ virtual Return<void> onScanMessage(FrontendScanMessageType /* type */,
+ const FrontendScanMessage& /* message */) override {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ mScanMessageReceived = true;
+ mMsgCondition.signal();
+ return Void();
+ };
+
void testOnEvent(sp<IFrontend>& frontend, FrontendSettings settings);
void testOnDiseqcMessage(sp<IFrontend>& frontend, FrontendSettings settings);
private:
bool mEventReceived = false;
bool mDiseqcMessageReceived = false;
+ bool mScanMessageReceived = false;
FrontendEventType mEventType;
hidl_vec<uint8_t> mEventMessage;
android::Mutex mMsgLock;
@@ -185,8 +215,14 @@
public:
virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent) override {
android::Mutex::Autolock autoLock(mMsgLock);
- mFilterEventReceived = true;
- mFilterEvent = filterEvent;
+ // 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
+ mFilterIdToEvent[filterEvent.filterId] = filterEvent;
+ readFilterEventData(filterEvent.filterId);
+ mPidFilterOutputCount++;
+ // mFilterIdToMQ.erase(filterEvent.filterId);
+
+ // startFilterEventThread(filterEvent);
mMsgCondition.signal();
return Void();
}
@@ -196,117 +232,235 @@
return Void();
}
+ virtual Return<void> onOutputStatus(DemuxOutputStatus /*status*/) override { return Void(); }
+
+ virtual Return<void> onInputStatus(DemuxInputStatus status) override {
+ // android::Mutex::Autolock autoLock(mMsgLock);
+ ALOGW("[vts] input status %d", status);
+ switch (status) {
+ case DemuxInputStatus::SPACE_EMPTY:
+ case DemuxInputStatus::SPACE_ALMOST_EMPTY:
+ ALOGW("[vts] keep inputing %d", status);
+ mKeepWritingInputFMQ = true;
+ break;
+ case DemuxInputStatus::SPACE_ALMOST_FULL:
+ case DemuxInputStatus::SPACE_FULL:
+ ALOGW("[vts] stop inputing %d", status);
+ mKeepWritingInputFMQ = false;
+ break;
+ }
+ return Void();
+ }
+
void testOnFilterEvent(uint32_t filterId);
- void testOnSectionFilterEvent(sp<IDemux>& demux, uint32_t filterId,
- FilterMQDesc& filterMQDescriptor);
- void testOnPesFilterEvent(sp<IDemux>& demux, uint32_t filterId,
- FilterMQDesc& filterMQDescriptor);
- void readAndCompareSectionEventData();
- void readAndComparePesEventData();
+ void testFilterDataOutput();
+ void stopInputThread();
+
+ void startPlaybackInputThread(InputConf inputConf, MQDesc& inputMQDescriptor);
+ void startFilterEventThread(DemuxFilterEvent event);
+ static void* __threadLoopInput(void* threadArgs);
+ static void* __threadLoopFilter(void* threadArgs);
+ void inputThreadLoop(InputConf* inputConf, bool* keepWritingInputFMQ);
+ void filterThreadLoop(DemuxFilterEvent& event);
+
+ void updateFilterMQ(uint32_t filterId, MQDesc& filterMQDescriptor);
+ void updateGoldenOutputMap(uint32_t filterId, string goldenOutputFile);
+ bool readFilterEventData(uint32_t filterId);
private:
- bool mFilterEventReceived = false;
- std::vector<uint8_t> mDataOutputBuffer;
- std::unique_ptr<FilterMQ> mFilterMQ;
+ struct InputThreadArgs {
+ DemuxCallback* user;
+ InputConf* inputConf;
+ bool* keepWritingInputFMQ;
+ };
+ struct FilterThreadArgs {
+ DemuxCallback* user;
+ DemuxFilterEvent event;
+ };
uint16_t mDataLength = 0;
- DemuxFilterEvent mFilterEvent;
+ std::vector<uint8_t> mDataOutputBuffer;
+
+ bool mFilterEventReceived;
+ std::map<uint32_t, string> mFilterIdToGoldenOutput;
+
+ std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterIdToMQ;
+ std::unique_ptr<FilterMQ> mInputMQ;
+ std::map<uint32_t, EventFlag*> mFilterIdToMQEventFlag;
+ std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
+ EventFlag* mInputMQEventFlag;
+
android::Mutex mMsgLock;
- android::Mutex mReadLock;
+ android::Mutex mFilterOutputLock;
+ android::Mutex mInputThreadLock;
android::Condition mMsgCondition;
- EventFlag* mFilterMQEventFlag;
+ android::Condition mFilterOutputCondition;
+
+ bool mKeepWritingInputFMQ = true;
+ bool mInputThreadRunning;
+ pthread_t mInputThread;
+ pthread_t mFilterThread;
+
+ int mPidFilterOutputCount = 0;
};
-void DemuxCallback::testOnFilterEvent(uint32_t filterId) {
+void DemuxCallback::startPlaybackInputThread(InputConf inputConf, MQDesc& inputMQDescriptor) {
+ mInputMQ = std::make_unique<FilterMQ>(inputMQDescriptor, true /* resetPointers */);
+ EXPECT_TRUE(mInputMQ);
+ struct InputThreadArgs* threadArgs =
+ (struct InputThreadArgs*)malloc(sizeof(struct InputThreadArgs));
+ threadArgs->user = this;
+ threadArgs->inputConf = &inputConf;
+ threadArgs->keepWritingInputFMQ = &mKeepWritingInputFMQ;
+
+ pthread_create(&mInputThread, NULL, __threadLoopInput, (void*)threadArgs);
+ pthread_setname_np(mInputThread, "test_playback_input_loop");
+}
+
+void DemuxCallback::startFilterEventThread(DemuxFilterEvent event) {
+ struct FilterThreadArgs* threadArgs =
+ (struct FilterThreadArgs*)malloc(sizeof(struct FilterThreadArgs));
+ threadArgs->user = this;
+ threadArgs->event = event;
+
+ pthread_create(&mFilterThread, NULL, __threadLoopFilter, (void*)threadArgs);
+ pthread_setname_np(mFilterThread, "test_playback_input_loop");
+}
+
+void DemuxCallback::testFilterDataOutput() {
android::Mutex::Autolock autoLock(mMsgLock);
- while (!mFilterEventReceived) {
+ while (mPidFilterOutputCount < 1) {
if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
- EXPECT_TRUE(false) << "filter event not received within timeout";
+ EXPECT_TRUE(false) << "filter output matching pid does not output within timeout";
return;
}
}
- // Reset the filter event recieved flag
- mFilterEventReceived = false;
- // Check if filter id match
- EXPECT_TRUE(filterId == mFilterEvent.filterId) << "filter id match";
+ mPidFilterOutputCount = 0;
+ ALOGW("[vts] pass and stop");
}
-void DemuxCallback::testOnSectionFilterEvent(sp<IDemux>& demux, uint32_t filterId,
- FilterMQDesc& filterMQDescriptor) {
- Result status;
- // Create MQ to read the output into the local buffer
- mFilterMQ = std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
- EXPECT_TRUE(mFilterMQ);
+void DemuxCallback::stopInputThread() {
+ mInputThreadRunning = false;
+ mKeepWritingInputFMQ = false;
+
+ android::Mutex::Autolock autoLock(mInputThreadLock);
+}
+
+void DemuxCallback::updateFilterMQ(uint32_t filterId, MQDesc& filterMQDescriptor) {
+ mFilterIdToMQ[filterId] =
+ std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
+ EXPECT_TRUE(mFilterIdToMQ[filterId]);
+ EXPECT_TRUE(EventFlag::createEventFlag(mFilterIdToMQ[filterId]->getEventFlagWord(),
+ &mFilterIdToMQEventFlag[filterId]) == android::OK);
+}
+
+void DemuxCallback::updateGoldenOutputMap(uint32_t filterId, string goldenOutputFile) {
+ mFilterIdToGoldenOutput[filterId] = goldenOutputFile;
+}
+
+void* DemuxCallback::__threadLoopInput(void* threadArgs) {
+ DemuxCallback* const self =
+ static_cast<DemuxCallback*>(((struct InputThreadArgs*)threadArgs)->user);
+ self->inputThreadLoop(((struct InputThreadArgs*)threadArgs)->inputConf,
+ ((struct InputThreadArgs*)threadArgs)->keepWritingInputFMQ);
+ return 0;
+}
+
+void DemuxCallback::inputThreadLoop(InputConf* inputConf, bool* keepWritingInputFMQ) {
+ android::Mutex::Autolock autoLock(mInputThreadLock);
+ mInputThreadRunning = true;
+
// Create the EventFlag that is used to signal the HAL impl that data have been
- // read the Filter FMQ
- EXPECT_TRUE(EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag) ==
+ // written into the Input FMQ
+ EventFlag* inputMQEventFlag;
+ EXPECT_TRUE(EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &inputMQEventFlag) ==
android::OK);
- // Start filter
- status = demux->startFilter(filterId);
- EXPECT_EQ(status, Result::SUCCESS);
- // Test start filter and receive callback event
- for (int i = 0; i < SECTION_READ_COUNT; i++) {
- testOnFilterEvent(filterId);
- // checksum of mDataOutputBuffer and Input golden input
- readAndCompareSectionEventData();
+
+ // open the stream and get its length
+ std::ifstream inputData(inputConf->inputDataFile, std::ifstream::binary);
+ int writeSize = inputConf->setting.packetSize * 6;
+ char* buffer = new char[writeSize];
+ ALOGW("[vts] input thread loop start %s", inputConf->inputDataFile.c_str());
+ if (!inputData.is_open()) {
+ mInputThreadRunning = false;
+ ALOGW("[vts] Error %s", strerror(errno));
}
-}
-void DemuxCallback::testOnPesFilterEvent(sp<IDemux>& demux, uint32_t filterId,
- FilterMQDesc& filterMQDescriptor) {
- Result status;
- // Create MQ to read the output into the local buffer
- mFilterMQ = std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
- EXPECT_TRUE(mFilterMQ);
- // Create the EventFlag that is used to signal the HAL impl that data have been
- // read the Filter FMQ
- EXPECT_TRUE(EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag) ==
- android::OK);
- // Start filter
- status = demux->startFilter(filterId);
- EXPECT_EQ(status, Result::SUCCESS);
- // Test start filter and receive callback event
- testOnFilterEvent(filterId);
- // checksum of mDataOutputBuffer and Input golden input
- readAndComparePesEventData();
-}
-
-void DemuxCallback::readAndCompareSectionEventData() {
- bool result = false;
- for (int i = 0; i < mFilterEvent.events.size(); i++) {
- DemuxFilterSectionEvent event = mFilterEvent.events[i].section();
- mDataLength = event.dataLength;
- EXPECT_TRUE(mDataLength == goldenDataInputBuffer.size()) << "buffer size does not match";
-
- mDataOutputBuffer.resize(mDataLength);
- result = mFilterMQ->read(mDataOutputBuffer.data(), mDataLength);
- EXPECT_TRUE(result) << "can't read from Filter MQ";
-
- for (int i = 0; i < mDataLength; i++) {
- EXPECT_TRUE(goldenDataInputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
+ while (mInputThreadRunning) {
+ // move the stream pointer for packet size * 6 every read until the end
+ while (*keepWritingInputFMQ) {
+ inputData.read(buffer, writeSize);
+ if (!inputData) {
+ int leftSize = inputData.gcount();
+ if (leftSize == 0) {
+ mInputThreadRunning = false;
+ break;
+ }
+ inputData.clear();
+ inputData.read(buffer, leftSize);
+ // Write the left over of the input data and quit the thread
+ if (leftSize > 0) {
+ EXPECT_TRUE(mInputMQ->write((unsigned char*)&buffer[0], leftSize));
+ inputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+ }
+ mInputThreadRunning = false;
+ break;
+ }
+ // Write input FMQ and notify the Tuner Implementation
+ EXPECT_TRUE(mInputMQ->write((unsigned char*)&buffer[0], writeSize));
+ inputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+ inputData.seekg(writeSize, inputData.cur);
+ sleep(1);
}
}
- if (result) {
- mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
- }
+
+ ALOGW("[vts] Input thread end.");
+
+ delete[] buffer;
+ inputData.close();
}
-void DemuxCallback::readAndComparePesEventData() {
- // TODO handle multiple events in one filter callback event
- DemuxFilterPesEvent event = mFilterEvent.events[0].pes();
- mDataLength = event.dataLength;
- EXPECT_TRUE(mDataLength == goldenDataInputBuffer.size()) << "buffer size does not match";
+void* DemuxCallback::__threadLoopFilter(void* threadArgs) {
+ DemuxCallback* const self =
+ static_cast<DemuxCallback*>(((struct FilterThreadArgs*)threadArgs)->user);
+ self->filterThreadLoop(((struct FilterThreadArgs*)threadArgs)->event);
+ return 0;
+}
- mDataOutputBuffer.resize(mDataLength);
- bool result = mFilterMQ->read(mDataOutputBuffer.data(), mDataLength);
- EXPECT_TRUE(result) << "can't read from Filter MQ";
+void DemuxCallback::filterThreadLoop(DemuxFilterEvent& /* event */) {
+ android::Mutex::Autolock autoLock(mFilterOutputLock);
+ // Read from mFilterIdToMQ[event.filterId] per event and filter type
- if (result) {
- mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+ // Assemble to filterOutput[filterId]
+
+ // check if filterOutput[filterId] matches goldenOutput[filterId]
+
+ // If match, remove filterId entry from MQ map
+
+ // end thread
+}
+
+bool DemuxCallback::readFilterEventData(uint32_t filterId) {
+ bool result = false;
+ DemuxFilterEvent filterEvent = mFilterIdToEvent[filterId];
+ ALOGW("[vts] reading from filter FMQ %d", filterId);
+ // todo separate filter handlers
+ for (int i = 0; i < filterEvent.events.size(); i++) {
+ DemuxFilterPesEvent event = filterEvent.events[i].pes();
+ mDataLength = event.dataLength;
+ // EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not
+ // match";
+
+ mDataOutputBuffer.resize(mDataLength);
+ result = mFilterIdToMQ[filterId]->read(mDataOutputBuffer.data(), mDataLength);
+ EXPECT_TRUE(result) << "can't read from Filter MQ";
+
+ /*for (int i = 0; i < mDataLength; i++) {
+ EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
+ }*/
}
-
- for (int i = 0; i < mDataLength; i++) {
- EXPECT_TRUE(goldenDataInputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
- }
+ mFilterIdToMQEventFlag[filterId]->wake(
+ static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+ return result;
}
// Test environment for Tuner HIDL HAL.
@@ -341,24 +495,36 @@
sp<IDescrambler> mDescrambler;
sp<IDemux> mDemux;
sp<DemuxCallback> mDemuxCallback;
- FilterMQDesc mFilterMQDescriptor;
+ MQDesc mFilterMQDescriptor;
+ MQDesc mInputMQDescriptor;
+ vector<uint32_t> mUsedFilterIds;
+
uint32_t mDemuxId;
uint32_t mFilterId;
+ pthread_t mInputThread;
+ bool mInputThreadRunning;
+
::testing::AssertionResult createFrontend(int32_t frontendId);
::testing::AssertionResult tuneFrontend(int32_t frontendId);
::testing::AssertionResult stopTuneFrontend(int32_t frontendId);
::testing::AssertionResult closeFrontend(int32_t frontendId);
::testing::AssertionResult createDemux();
- ::testing::AssertionResult createDemuxWithFrontend(int32_t frontendId);
- ::testing::AssertionResult addSectionFilterToDemux();
- ::testing::AssertionResult addPesFilterToDemux();
- ::testing::AssertionResult getFilterMQDescriptor(sp<IDemux>& demux, const uint32_t filterId);
- ::testing::AssertionResult readSectionFilterDataOutput();
- ::testing::AssertionResult readPesFilterDataOutput();
+ ::testing::AssertionResult createDemuxWithFrontend(int32_t frontendId,
+ FrontendSettings settings);
+ ::testing::AssertionResult getInputMQDescriptor();
+ ::testing::AssertionResult addInputToDemux(DemuxInputSettings setting);
+ ::testing::AssertionResult addFilterToDemux(DemuxFilterType type, DemuxFilterSettings setting);
+ ::testing::AssertionResult getFilterMQDescriptor(const uint32_t filterId);
::testing::AssertionResult closeDemux();
::testing::AssertionResult createDescrambler();
::testing::AssertionResult closeDescrambler();
+
+ ::testing::AssertionResult playbackDataFlowTest(vector<FilterConf> filterConf,
+ InputConf inputConf,
+ vector<string> goldenOutputFiles);
+ ::testing::AssertionResult broadcastDataFlowTest(vector<FilterConf> filterConf,
+ vector<string> goldenOutputFiles);
};
::testing::AssertionResult TunerHidlTest::createFrontend(int32_t frontendId) {
@@ -389,13 +555,11 @@
.frequency = 0,
.modulation = FrontendAtscModulation::UNDEFINED,
};
- frontendSettings.atsc() = frontendAtscSettings;
+ frontendSettings.atsc(frontendAtscSettings);
mFrontendCallback->testOnEvent(mFrontend, frontendSettings);
FrontendDvbtSettings frontendDvbtSettings{
.frequency = 0,
- .modulation = FrontendAtscModulation::UNDEFINED,
- .fec = FrontendInnerFec::FEC_UNDEFINED,
};
frontendSettings.dvbt(frontendDvbtSettings);
mFrontendCallback->testOnEvent(mFrontend, frontendSettings);
@@ -405,7 +569,7 @@
::testing::AssertionResult TunerHidlTest::stopTuneFrontend(int32_t frontendId) {
Result status;
- if (createFrontend(frontendId) == ::testing::AssertionFailure()) {
+ if (!mFrontend && createFrontend(frontendId) == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
@@ -415,11 +579,12 @@
::testing::AssertionResult TunerHidlTest::closeFrontend(int32_t frontendId) {
Result status;
- if (createFrontend(frontendId) == ::testing::AssertionFailure()) {
+ if (!mFrontend && createFrontend(frontendId) == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
status = mFrontend->close();
+ mFrontend = nullptr;
return ::testing::AssertionResult(status == Result::SUCCESS);
}
@@ -434,127 +599,33 @@
return ::testing::AssertionResult(status == Result::SUCCESS);
}
-::testing::AssertionResult TunerHidlTest::createDemuxWithFrontend(int32_t frontendId) {
+::testing::AssertionResult TunerHidlTest::createDemuxWithFrontend(int32_t frontendId,
+ FrontendSettings settings) {
Result status;
- if (createDemux() == ::testing::AssertionFailure()) {
+ if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
- if (createFrontend(frontendId) == ::testing::AssertionFailure()) {
+ if (!mFrontend && createFrontend(frontendId) == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
+ mFrontendCallback->testOnEvent(mFrontend, settings);
+
status = mDemux->setFrontendDataSource(frontendId);
return ::testing::AssertionResult(status == Result::SUCCESS);
}
-::testing::AssertionResult TunerHidlTest::addSectionFilterToDemux() {
- Result status;
-
- if (createDemux() == ::testing::AssertionFailure()) {
- return ::testing::AssertionFailure();
- }
-
- // Create demux callback
- mDemuxCallback = new DemuxCallback();
-
- // Add section filter to the local demux
- mDemux->addFilter(DemuxFilterType::SECTION, FMQ_SIZE_4K, mDemuxCallback,
- [&](Result result, uint32_t filterId) {
- mFilterId = filterId;
- status = result;
- });
-
- // Add another section filter to the local demux
- mDemux->addFilter(DemuxFilterType::SECTION, FMQ_SIZE_4K, mDemuxCallback,
- [&](Result result, uint32_t filterId) {
- mFilterId = filterId;
- status = result;
- });
-
- // TODO Test configure the filter
-
- return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::addPesFilterToDemux() {
- Result status;
-
- if (createDemux() == ::testing::AssertionFailure()) {
- return ::testing::AssertionFailure();
- }
-
- // Create demux callback
- mDemuxCallback = new DemuxCallback();
-
- // Add PES filter to the local demux
- mDemux->addFilter(DemuxFilterType::PES, FMQ_SIZE_4K, mDemuxCallback,
- [&](Result result, uint32_t filterId) {
- mFilterId = filterId;
- status = result;
- });
-
- // Add another PES filter to the local demux
- mDemux->addFilter(DemuxFilterType::PES, FMQ_SIZE_4K, mDemuxCallback,
- [&](Result result, uint32_t filterId) {
- mFilterId = filterId;
- status = result;
- });
-
- // TODO Test configure the filter
-
- return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::getFilterMQDescriptor(sp<IDemux>& demux,
- const uint32_t filterId) {
- Result status;
-
- if (!demux) {
- return ::testing::AssertionFailure();
- }
-
- mDemux->getFilterQueueDesc(filterId, [&](Result result, const FilterMQDesc& filterMQDesc) {
- mFilterMQDescriptor = filterMQDesc;
- status = result;
- });
-
- return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::readSectionFilterDataOutput() {
- if (addSectionFilterToDemux() == ::testing::AssertionFailure() ||
- getFilterMQDescriptor(mDemux, mFilterId) == ::testing::AssertionFailure()) {
- return ::testing::AssertionFailure();
- }
-
- // Test start filter and read the output data
- mDemuxCallback->testOnSectionFilterEvent(mDemux, mFilterId, mFilterMQDescriptor);
-
- return ::testing::AssertionResult(true);
-}
-
-::testing::AssertionResult TunerHidlTest::readPesFilterDataOutput() {
- if (addPesFilterToDemux() == ::testing::AssertionFailure() ||
- getFilterMQDescriptor(mDemux, mFilterId) == ::testing::AssertionFailure()) {
- return ::testing::AssertionFailure();
- }
-
- // Test start filter and read the output data
- mDemuxCallback->testOnPesFilterEvent(mDemux, mFilterId, mFilterMQDescriptor);
-
- return ::testing::AssertionResult(true);
-}
-
::testing::AssertionResult TunerHidlTest::closeDemux() {
Result status;
- if (createDemux() == ::testing::AssertionFailure()) {
+ if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
status = mDemux->close();
+ mDemux = nullptr;
return ::testing::AssertionResult(status == Result::SUCCESS);
}
@@ -569,7 +640,7 @@
return ::testing::AssertionFailure();
}
- if (createDemux() == ::testing::AssertionFailure()) {
+ if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
@@ -585,14 +656,212 @@
::testing::AssertionResult TunerHidlTest::closeDescrambler() {
Result status;
- if (createDescrambler() == ::testing::AssertionFailure()) {
+ if (!mDescrambler && createDescrambler() == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
status = mDescrambler->close();
+ mDescrambler = nullptr;
return ::testing::AssertionResult(status == Result::SUCCESS);
}
+::testing::AssertionResult TunerHidlTest::addInputToDemux(DemuxInputSettings setting) {
+ Result status;
+
+ if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+
+ // Create demux callback
+ if (!mDemuxCallback) {
+ mDemuxCallback = new DemuxCallback();
+ }
+
+ // Add playback input to the local demux
+ status = mDemux->addInput(FMQ_SIZE_1M, mDemuxCallback);
+
+ if (status != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+
+ status = mDemux->configureInput(setting);
+
+ return ::testing::AssertionResult(status == Result::SUCCESS);
+}
+
+::testing::AssertionResult TunerHidlTest::getInputMQDescriptor() {
+ Result status;
+
+ if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+
+ mDemux->getInputQueueDesc([&](Result result, const MQDesc& inputMQDesc) {
+ mInputMQDescriptor = inputMQDesc;
+ status = result;
+ });
+
+ return ::testing::AssertionResult(status == Result::SUCCESS);
+}
+
+::testing::AssertionResult TunerHidlTest::addFilterToDemux(DemuxFilterType type,
+ DemuxFilterSettings setting) {
+ Result status;
+
+ if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+
+ // Create demux callback
+ if (!mDemuxCallback) {
+ mDemuxCallback = new DemuxCallback();
+ }
+
+ // Add filter to the local demux
+ mDemux->addFilter(type, FMQ_SIZE_4K, mDemuxCallback, [&](Result result, uint32_t filterId) {
+ // TODO use a map to save all the filter id and FMQ
+ mFilterId = filterId;
+ status = result;
+ });
+
+ if (status != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+
+ // Configure the filter
+ status = mDemux->configureFilter(mFilterId, setting);
+
+ return ::testing::AssertionResult(status == Result::SUCCESS);
+}
+
+::testing::AssertionResult TunerHidlTest::getFilterMQDescriptor(const uint32_t filterId) {
+ Result status;
+
+ if (!mDemux) {
+ return ::testing::AssertionFailure();
+ }
+
+ mDemux->getFilterQueueDesc(filterId, [&](Result result, const MQDesc& filterMQDesc) {
+ mFilterMQDescriptor = filterMQDesc;
+ status = result;
+ });
+
+ return ::testing::AssertionResult(status == Result::SUCCESS);
+}
+
+::testing::AssertionResult TunerHidlTest::playbackDataFlowTest(
+ vector<FilterConf> filterConf, InputConf inputConf, vector<string> /*goldenOutputFiles*/) {
+ Result status;
+ int filterIdsSize;
+ // Filter Configuration Module
+ for (int i = 0; i < filterConf.size(); i++) {
+ if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
+ ::testing::AssertionFailure() ||
+ // TODO use a map to save the FMQs/EvenFlags and pass to callback
+ getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+ filterIdsSize = mUsedFilterIds.size();
+ mUsedFilterIds.resize(filterIdsSize + 1);
+ mUsedFilterIds[filterIdsSize] = mFilterId;
+ mDemuxCallback->updateFilterMQ(mFilterId, mFilterMQDescriptor);
+ // mDemuxCallback->updateGoldenOutputMap(mFilterId, goldenOutputFiles[i]);
+ status = mDemux->startFilter(mFilterId);
+ if (status != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+
+ // Playback Input Module
+ DemuxInputSettings inputSetting = inputConf.setting;
+ if (addInputToDemux(inputSetting) == ::testing::AssertionFailure() ||
+ getInputMQDescriptor() == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+ mDemuxCallback->startPlaybackInputThread(inputConf, mInputMQDescriptor);
+ status = mDemux->startInput();
+ if (status != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+
+ // Data Verify Module
+ mDemuxCallback->testFilterDataOutput();
+ mDemuxCallback->stopInputThread();
+
+ // Clean Up Module
+ for (int i = 0; i <= filterIdsSize; i++) {
+ if (mDemux->stopFilter(mUsedFilterIds[i]) != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+ if (mDemux->stopInput() != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ return closeDemux();
+}
+
+::testing::AssertionResult TunerHidlTest::broadcastDataFlowTest(
+ vector<FilterConf> filterConf, vector<string> /*goldenOutputFiles*/) {
+ Result status;
+ hidl_vec<FrontendId> feIds;
+
+ mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+ status = result;
+ feIds = frontendIds;
+ });
+
+ if (feIds.size() == 0) {
+ ALOGW("[ WARN ] Frontend isn't available");
+ return ::testing::AssertionFailure();
+ }
+
+ FrontendDvbtSettings dvbt{
+ .frequency = 1000,
+ };
+ FrontendSettings settings;
+ settings.dvbt(dvbt);
+
+ if (createDemuxWithFrontend(feIds[0], settings) != ::testing::AssertionSuccess()) {
+ return ::testing::AssertionFailure();
+ }
+
+ int filterIdsSize;
+ // Filter Configuration Module
+ for (int i = 0; i < filterConf.size(); i++) {
+ if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
+ ::testing::AssertionFailure() ||
+ // TODO use a map to save the FMQs/EvenFlags and pass to callback
+ getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+ filterIdsSize = mUsedFilterIds.size();
+ mUsedFilterIds.resize(filterIdsSize + 1);
+ mUsedFilterIds[filterIdsSize] = mFilterId;
+ mDemuxCallback->updateFilterMQ(mFilterId, mFilterMQDescriptor);
+ status = mDemux->startFilter(mFilterId);
+ if (status != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+
+ // Data Verify Module
+ mDemuxCallback->testFilterDataOutput();
+
+ // Clean Up Module
+ for (int i = 0; i <= filterIdsSize; i++) {
+ if (mDemux->stopFilter(mUsedFilterIds[i]) != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+ if (mFrontend->stopTune() != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ return closeDemux();
+}
+
+/*
+ * API STATUS TESTS
+ */
TEST_F(TunerHidlTest, CreateFrontend) {
Result status;
hidl_vec<FrontendId> feIds;
@@ -673,13 +942,7 @@
}
}
-TEST_F(TunerHidlTest, CreateDemux) {
- description("Create Demux");
-
- ASSERT_TRUE(createDemux());
-}
-
-TEST_F(TunerHidlTest, CreateDemuxWithFrontend) {
+/*TEST_F(TunerHidlTest, CreateDemuxWithFrontend) {
Result status;
hidl_vec<FrontendId> feIds;
@@ -694,55 +957,100 @@
return;
}
+ FrontendDvbtSettings dvbt{
+ .frequency = 1000,
+ };
+ FrontendSettings settings;
+ settings.dvbt(dvbt);
+
for (size_t i = 0; i < feIds.size(); i++) {
- ASSERT_TRUE(createDemuxWithFrontend(feIds[i]));
+ ASSERT_TRUE(createDemuxWithFrontend(feIds[i], settings));
+ mFrontend->stopTune();
}
-}
+}*/
-TEST_F(TunerHidlTest, AddSectionFilterToDemux) {
- description("Add a section filter to a created demux");
- ASSERT_TRUE(addSectionFilterToDemux());
-}
-
-TEST_F(TunerHidlTest, AddPesFilterToDemux) {
- description("Add a pes filter to a created demux");
- ASSERT_TRUE(addPesFilterToDemux());
-}
-
-TEST_F(TunerHidlTest, GetFilterMQDescriptor) {
- description("Get MQ Descriptor from a created filter");
- ASSERT_TRUE(addSectionFilterToDemux());
- ASSERT_TRUE(getFilterMQDescriptor(mDemux, mFilterId));
-}
-
-TEST_F(TunerHidlTest, ReadSectionFilterOutput) {
- description("Read data output from FMQ of a Section Filter");
- ASSERT_TRUE(readSectionFilterDataOutput());
-}
-
-TEST_F(TunerHidlTest, ReadPesFilterOutput) {
- description("Read data output from FMQ of a PES Filter");
- ASSERT_TRUE(readPesFilterDataOutput());
+TEST_F(TunerHidlTest, CreateDemux) {
+ description("Create Demux");
+ ASSERT_TRUE(createDemux());
}
TEST_F(TunerHidlTest, CloseDemux) {
description("Close Demux");
-
ASSERT_TRUE(closeDemux());
}
TEST_F(TunerHidlTest, CreateDescrambler) {
description("Create Descrambler");
-
ASSERT_TRUE(createDescrambler());
}
TEST_F(TunerHidlTest, CloseDescrambler) {
description("Close Descrambler");
-
ASSERT_TRUE(closeDescrambler());
}
+/*
+ * DATA FLOW TESTS
+ */
+TEST_F(TunerHidlTest, PlaybackDataFlowWithPesFilterTest) {
+ description("Feed ts data from playback and configure pes filter to get output");
+
+ // todo modulize the filter conf parser
+ vector<FilterConf> filterConf;
+ filterConf.resize(1);
+
+ DemuxFilterSettings filterSetting;
+ DemuxFilterPesDataSettings pesFilterSetting{
+ .tpid = 18,
+ };
+ filterSetting.pesData(pesFilterSetting);
+ FilterConf pesFilterConf{
+ .type = DemuxFilterType::PES,
+ .setting = filterSetting,
+ };
+ filterConf[0] = pesFilterConf;
+
+ DemuxInputSettings inputSetting{
+ .statusMask = 0xf,
+ .lowThreshold = 0x1000,
+ .highThreshold = 0x07fff,
+ .dataFormat = DemuxDataFormat::TS,
+ .packetSize = 188,
+ };
+
+ InputConf inputConf{
+ .inputDataFile = "/vendor/etc/test1.ts",
+ .setting = inputSetting,
+ };
+
+ vector<string> goldenOutputFiles;
+
+ ASSERT_TRUE(playbackDataFlowTest(filterConf, inputConf, goldenOutputFiles));
+}
+
+TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
+ description("Feed ts data from frontend and test with PES filter");
+
+ // todo modulize the filter conf parser
+ vector<FilterConf> filterConf;
+ filterConf.resize(1);
+
+ DemuxFilterSettings filterSetting;
+ DemuxFilterPesDataSettings pesFilterSetting{
+ .tpid = 18,
+ };
+ filterSetting.pesData(pesFilterSetting);
+ FilterConf pesFilterConf{
+ .type = DemuxFilterType::PES,
+ .setting = filterSetting,
+ };
+ filterConf[0] = pesFilterConf;
+
+ vector<string> goldenOutputFiles;
+
+ ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles));
+}
+
} // namespace
int main(int argc, char** argv) {
diff --git a/wifi/hostapd/1.0/vts/functional/Android.bp b/wifi/hostapd/1.0/vts/functional/Android.bp
index 93867d2..5645019 100644
--- a/wifi/hostapd/1.0/vts/functional/Android.bp
+++ b/wifi/hostapd/1.0/vts/functional/Android.bp
@@ -26,7 +26,6 @@
"android.hardware.wifi.hostapd@1.0",
"android.hardware.wifi.hostapd@1.1",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
@@ -46,7 +45,6 @@
"android.hardware.wifi.hostapd@1.0",
"android.hardware.wifi.hostapd@1.1",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
diff --git a/wifi/hostapd/1.1/vts/functional/Android.bp b/wifi/hostapd/1.1/vts/functional/Android.bp
index bbf5246..02ec2e6 100644
--- a/wifi/hostapd/1.1/vts/functional/Android.bp
+++ b/wifi/hostapd/1.1/vts/functional/Android.bp
@@ -27,7 +27,6 @@
"android.hardware.wifi.hostapd@1.0",
"android.hardware.wifi.hostapd@1.1",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
@@ -48,7 +47,6 @@
"android.hardware.wifi.hostapd@1.0",
"android.hardware.wifi.hostapd@1.1",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
diff --git a/wifi/supplicant/1.0/vts/functional/Android.bp b/wifi/supplicant/1.0/vts/functional/Android.bp
index bdccac1..ba79738 100644
--- a/wifi/supplicant/1.0/vts/functional/Android.bp
+++ b/wifi/supplicant/1.0/vts/functional/Android.bp
@@ -26,7 +26,6 @@
"android.hardware.wifi.supplicant@1.0",
"android.hardware.wifi.supplicant@1.1",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
@@ -48,7 +47,6 @@
"android.hardware.wifi.supplicant@1.0",
"android.hardware.wifi.supplicant@1.1",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
@@ -69,7 +67,6 @@
"android.hardware.wifi.supplicant@1.0",
"android.hardware.wifi.supplicant@1.1",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
diff --git a/wifi/supplicant/1.1/vts/functional/Android.bp b/wifi/supplicant/1.1/vts/functional/Android.bp
index 353ae4b..8457532 100644
--- a/wifi/supplicant/1.1/vts/functional/Android.bp
+++ b/wifi/supplicant/1.1/vts/functional/Android.bp
@@ -27,7 +27,6 @@
"android.hardware.wifi.supplicant@1.0",
"android.hardware.wifi.supplicant@1.1",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
@@ -51,7 +50,6 @@
"android.hardware.wifi.supplicant@1.1",
"android.hardware.wifi@1.0",
"android.hardware.wifi@1.1",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
diff --git a/wifi/supplicant/1.2/vts/functional/Android.bp b/wifi/supplicant/1.2/vts/functional/Android.bp
index 1b970e1..b7949d1 100644
--- a/wifi/supplicant/1.2/vts/functional/Android.bp
+++ b/wifi/supplicant/1.2/vts/functional/Android.bp
@@ -29,7 +29,6 @@
"android.hardware.wifi.supplicant@1.1",
"android.hardware.wifi.supplicant@1.2",
"android.hardware.wifi@1.0",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
@@ -54,7 +53,6 @@
"android.hardware.wifi.supplicant@1.2",
"android.hardware.wifi@1.0",
"android.hardware.wifi@1.1",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",
@@ -79,7 +77,6 @@
"android.hardware.wifi.supplicant@1.2",
"android.hardware.wifi@1.0",
"android.hardware.wifi@1.1",
- "libcrypto",
"libgmock",
"libwifi-system",
"libwifi-system-iface",