Merge "Add vintf_fragments to keymaster@4.1-service"
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index 33157a6..9d4e55c 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -23,8 +23,9 @@
"kernel_config_o_3.18",
"kernel_config_o_4.4",
"kernel_config_o_4.9",
- ]
+ ],
}
+
vintf_compatibility_matrix {
name: "framework_compatibility_matrix.1.xml",
stem: "compatibility_matrix.1.xml",
@@ -35,8 +36,9 @@
"kernel_config_o_3.18",
"kernel_config_o_4.4",
"kernel_config_o_4.9",
- ]
+ ],
}
+
vintf_compatibility_matrix {
name: "framework_compatibility_matrix.2.xml",
stem: "compatibility_matrix.2.xml",
@@ -47,7 +49,7 @@
"kernel_config_o_mr1_3.18",
"kernel_config_o_mr1_4.4",
"kernel_config_o_mr1_4.9",
- ]
+ ],
}
vintf_compatibility_matrix {
@@ -60,7 +62,7 @@
"kernel_config_p_4.4",
"kernel_config_p_4.9",
"kernel_config_p_4.14",
- ]
+ ],
}
vintf_compatibility_matrix {
@@ -73,7 +75,20 @@
"kernel_config_q_4.9",
"kernel_config_q_4.14",
"kernel_config_q_4.19",
- ]
+ ],
+}
+
+vintf_compatibility_matrix {
+ name: "framework_compatibility_matrix.5.xml",
+ stem: "compatibility_matrix.5.xml",
+ srcs: [
+ "compatibility_matrix.5.xml",
+ ],
+ kernel_configs: [
+ "kernel_config_r_4.14",
+ "kernel_config_r_4.19",
+ "kernel_config_r_5.4",
+ ],
}
vintf_compatibility_matrix {
@@ -83,8 +98,8 @@
"compatibility_matrix.current.xml",
],
kernel_configs: [
- "kernel_config_r_4.14",
- "kernel_config_r_4.19",
- "kernel_config_r_5.4",
- ]
+ "kernel_config_current_4.14",
+ "kernel_config_current_4.19",
+ "kernel_config_current_5.4",
+ ],
}
diff --git a/compatibility_matrices/Android.mk b/compatibility_matrices/Android.mk
index 6d204cb..96191c8 100644
--- a/compatibility_matrices/Android.mk
+++ b/compatibility_matrices/Android.mk
@@ -97,6 +97,7 @@
framework_compatibility_matrix.2.xml \
framework_compatibility_matrix.3.xml \
framework_compatibility_matrix.4.xml \
+ framework_compatibility_matrix.5.xml \
framework_compatibility_matrix.current.xml \
framework_compatibility_matrix.device.xml \
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
new file mode 100644
index 0000000..cc4f0cd
--- /dev/null
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -0,0 +1,521 @@
+<compatibility-matrix version="1.0" type="framework" level="5">
+ <hal format="hidl" optional="true">
+ <name>android.hardware.atrace</name>
+ <version>1.0</version>
+ <interface>
+ <name>IAtraceDevice</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="false">
+ <name>android.hardware.audio</name>
+ <version>6.0</version>
+ <interface>
+ <name>IDevicesFactory</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="false">
+ <name>android.hardware.audio.effect</name>
+ <version>6.0</version>
+ <interface>
+ <name>IEffectsFactory</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.authsecret</name>
+ <version>1.0</version>
+ <interface>
+ <name>IAuthSecret</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.automotive.audiocontrol</name>
+ <version>1.0</version>
+ <interface>
+ <name>IAudioControl</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.automotive.evs</name>
+ <version>1.0</version>
+ <interface>
+ <name>IEvsEnumerator</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.automotive.vehicle</name>
+ <version>2.0</version>
+ <interface>
+ <name>IVehicle</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.biometrics.face</name>
+ <version>1.0</version>
+ <interface>
+ <name>IBiometricsFace</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.biometrics.fingerprint</name>
+ <version>2.1</version>
+ <interface>
+ <name>IBiometricsFingerprint</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.bluetooth</name>
+ <version>1.0-1</version>
+ <interface>
+ <name>IBluetoothHci</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.bluetooth.audio</name>
+ <version>2.0</version>
+ <interface>
+ <name>IBluetoothAudioProvidersFactory</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.boot</name>
+ <version>1.1</version>
+ <interface>
+ <name>IBootControl</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.broadcastradio</name>
+ <version>1.0-1</version>
+ <interface>
+ <name>IBroadcastRadioFactory</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.broadcastradio</name>
+ <version>2.0</version>
+ <interface>
+ <name>IBroadcastRadio</name>
+ <regex-instance>.*</regex-instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.camera.provider</name>
+ <version>2.4-5</version>
+ <interface>
+ <name>ICameraProvider</name>
+ <regex-instance>[^/]+/[0-9]+</regex-instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.cas</name>
+ <version>1.1</version>
+ <interface>
+ <name>IMediaCasService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.configstore</name>
+ <version>1.1</version>
+ <interface>
+ <name>ISurfaceFlingerConfigs</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.confirmationui</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfirmationUI</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.contexthub</name>
+ <version>1.0</version>
+ <interface>
+ <name>IContexthub</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.drm</name>
+ <version>1.0-2</version>
+ <interface>
+ <name>ICryptoFactory</name>
+ <regex-instance>.*</regex-instance>
+ </interface>
+ <interface>
+ <name>IDrmFactory</name>
+ <regex-instance>.*</regex-instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.dumpstate</name>
+ <version>1.1</version>
+ <interface>
+ <name>IDumpstateDevice</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="false">
+ <name>android.hardware.gatekeeper</name>
+ <version>1.0</version>
+ <interface>
+ <name>IGatekeeper</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.gnss</name>
+ <version>2.0</version>
+ <interface>
+ <name>IGnss</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="false">
+ <name>android.hardware.graphics.allocator</name>
+ <version>2.0</version>
+ <version>3.0</version>
+ <interface>
+ <name>IAllocator</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="false">
+ <name>android.hardware.graphics.composer</name>
+ <version>2.1-3</version>
+ <interface>
+ <name>IComposer</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="false">
+ <name>android.hardware.graphics.mapper</name>
+ <version>2.1</version>
+ <version>3.0</version>
+ <interface>
+ <name>IMapper</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="false">
+ <name>android.hardware.health</name>
+ <version>2.1</version>
+ <interface>
+ <name>IHealth</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.health.storage</name>
+ <version>1.0</version>
+ <interface>
+ <name>IStorage</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.identity</name>
+ <interface>
+ <name>IIdentityCredentialStore</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.ir</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConsumerIr</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.input.classifier</name>
+ <version>1.0</version>
+ <interface>
+ <name>IInputClassifier</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="false">
+ <name>android.hardware.keymaster</name>
+ <version>3.0</version>
+ <version>4.0-1</version>
+ <interface>
+ <name>IKeymasterDevice</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.keymaster</name>
+ <version>4.0-1</version>
+ <interface>
+ <name>IKeymasterDevice</name>
+ <instance>strongbox</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.light</name>
+ <version>2.0</version>
+ <interface>
+ <name>ILight</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.light</name>
+ <interface>
+ <name>ILights</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IComponentStore</name>
+ <regex-instance>default[0-9]*</regex-instance>
+ <regex-instance>vendor[0-9]*_software</regex-instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.media.omx</name>
+ <version>1.0</version>
+ <interface>
+ <name>IOmx</name>
+ <instance>default</instance>
+ </interface>
+ <interface>
+ <name>IOmxStore</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.memtrack</name>
+ <version>1.0</version>
+ <interface>
+ <name>IMemtrack</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.neuralnetworks</name>
+ <version>1.0-3</version>
+ <interface>
+ <name>IDevice</name>
+ <regex-instance>.*</regex-instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.nfc</name>
+ <version>1.2</version>
+ <interface>
+ <name>INfc</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.oemlock</name>
+ <version>1.0</version>
+ <interface>
+ <name>IOemLock</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="false">
+ <name>android.hardware.power</name>
+ <interface>
+ <name>IPower</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.power.stats</name>
+ <version>1.0</version>
+ <interface>
+ <name>IPowerStats</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.radio</name>
+ <version>1.5</version>
+ <interface>
+ <name>IRadio</name>
+ <instance>slot1</instance>
+ <instance>slot2</instance>
+ <instance>slot3</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.radio</name>
+ <version>1.2</version>
+ <interface>
+ <name>ISap</name>
+ <instance>slot1</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.radio.config</name>
+ <!--
+ See compatibility_matrix.4.xml on versioning of radio config HAL.
+ -->
+ <version>1.1</version>
+ <interface>
+ <name>IRadioConfig</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.renderscript</name>
+ <version>1.0</version>
+ <interface>
+ <name>IDevice</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.secure_element</name>
+ <version>1.0-2</version>
+ <interface>
+ <name>ISecureElement</name>
+ <regex-instance>eSE[1-9][0-9]*</regex-instance>
+ <regex-instance>SIM[1-9][0-9]*</regex-instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.sensors</name>
+ <version>1.0</version>
+ <version>2.0</version>
+ <interface>
+ <name>ISensors</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.soundtrigger</name>
+ <version>2.0-2</version>
+ <interface>
+ <name>ISoundTriggerHw</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.tetheroffload.config</name>
+ <version>1.0</version>
+ <interface>
+ <name>IOffloadConfig</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.tetheroffload.control</name>
+ <version>1.0</version>
+ <interface>
+ <name>IOffloadControl</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.thermal</name>
+ <version>2.0</version>
+ <interface>
+ <name>IThermal</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.tv.cec</name>
+ <version>1.0</version>
+ <interface>
+ <name>IHdmiCec</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.tv.input</name>
+ <version>1.0</version>
+ <interface>
+ <name>ITvInput</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.usb</name>
+ <version>1.0-2</version>
+ <interface>
+ <name>IUsb</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.usb.gadget</name>
+ <version>1.0</version>
+ <interface>
+ <name>IUsbGadget</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.vibrator</name>
+ <interface>
+ <name>IVibrator</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.vr</name>
+ <version>1.0</version>
+ <interface>
+ <name>IVr</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.weaver</name>
+ <version>1.0</version>
+ <interface>
+ <name>IWeaver</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.wifi</name>
+ <version>1.0-3</version>
+ <interface>
+ <name>IWifi</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.wifi.hostapd</name>
+ <version>1.0-1</version>
+ <interface>
+ <name>IHostapd</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.wifi.supplicant</name>
+ <version>1.0-2</version>
+ <interface>
+ <name>ISupplicant</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</compatibility-matrix>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index cc4f0cd..ec1416a 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -1,4 +1,4 @@
-<compatibility-matrix version="1.0" type="framework" level="5">
+<compatibility-matrix version="1.0" type="framework" level="6">
<hal format="hidl" optional="true">
<name>android.hardware.atrace</name>
<version>1.0</version>
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index e28605d..ae1e3a2 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -125,7 +125,9 @@
// Test driver for those generated from ml/nn/runtime/test/spec
void Execute(const sp<IDevice>& device, const TestModel& testModel) {
const Model model = createModel(testModel);
- const Request request = createRequest(testModel);
+
+ ExecutionContext context;
+ const Request request = context.createRequest(testModel);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
@@ -143,7 +145,7 @@
ASSERT_EQ(ErrorStatus::NONE, executionCallback->getStatus());
// Retrieve execution results.
- const std::vector<TestBuffer> outputs = getOutputBuffers(request);
+ const std::vector<TestBuffer> outputs = context.getOutputBuffers(request);
// We want "close-enough" results.
checkResults(testModel, outputs);
@@ -158,6 +160,10 @@
return TestModelManager::get().getTestModels(filter);
}
+std::vector<NamedModel> getNamedModels(const FilterNameFn& 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));
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
index f230a02..1a55c2f 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
@@ -37,6 +37,9 @@
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
std::vector<NamedModel> getNamedModels(const FilterFn& filter);
+using FilterNameFn = std::function<bool(const std::string&)>;
+std::vector<NamedModel> getNamedModels(const FilterNameFn& filter);
+
std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
diff --git a/neuralnetworks/1.0/vts/functional/Utils.cpp b/neuralnetworks/1.0/vts/functional/Utils.cpp
index 0dba85a..3613e69 100644
--- a/neuralnetworks/1.0/vts/functional/Utils.cpp
+++ b/neuralnetworks/1.0/vts/functional/Utils.cpp
@@ -21,10 +21,13 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware_buffer.h>
#include <android/hidl/allocator/1.0/IAllocator.h>
#include <android/hidl/memory/1.0/IMemory.h>
#include <hidlmemory/mapping.h>
+#include <vndk/hardware_buffer.h>
+#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#include <vector>
@@ -37,10 +40,64 @@
using V1_0::Request;
using V1_0::RequestArgument;
-constexpr uint32_t kInputPoolIndex = 0;
-constexpr uint32_t kOutputPoolIndex = 1;
+std::unique_ptr<TestAshmem> TestAshmem::create(uint32_t size) {
+ auto ashmem = std::make_unique<TestAshmem>(size);
+ return ashmem->mIsValid ? std::move(ashmem) : nullptr;
+}
-Request createRequest(const TestModel& testModel) {
+void TestAshmem::initialize(uint32_t size) {
+ mIsValid = false;
+ ASSERT_GT(size, 0);
+ mHidlMemory = nn::allocateSharedMemory(size);
+ ASSERT_TRUE(mHidlMemory.valid());
+ mMappedMemory = mapMemory(mHidlMemory);
+ ASSERT_NE(mMappedMemory, nullptr);
+ mPtr = static_cast<uint8_t*>(static_cast<void*>(mMappedMemory->getPointer()));
+ ASSERT_NE(mPtr, nullptr);
+ mIsValid = true;
+}
+
+std::unique_ptr<TestBlobAHWB> TestBlobAHWB::create(uint32_t size) {
+ auto ahwb = std::make_unique<TestBlobAHWB>(size);
+ return ahwb->mIsValid ? std::move(ahwb) : nullptr;
+}
+
+void TestBlobAHWB::initialize(uint32_t size) {
+ mIsValid = false;
+ ASSERT_GT(size, 0);
+ const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+ const AHardwareBuffer_Desc desc = {
+ .width = size,
+ .height = 1,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB,
+ .usage = usage,
+ .stride = size,
+ };
+ ASSERT_EQ(AHardwareBuffer_allocate(&desc, &mAhwb), 0);
+ ASSERT_NE(mAhwb, nullptr);
+
+ void* buffer = nullptr;
+ ASSERT_EQ(AHardwareBuffer_lock(mAhwb, usage, -1, nullptr, &buffer), 0);
+ ASSERT_NE(buffer, nullptr);
+ mPtr = static_cast<uint8_t*>(buffer);
+
+ const native_handle_t* handle = AHardwareBuffer_getNativeHandle(mAhwb);
+ ASSERT_NE(handle, nullptr);
+ mHidlMemory = hidl_memory("hardware_buffer_blob", handle, desc.width);
+ mIsValid = true;
+}
+
+TestBlobAHWB::~TestBlobAHWB() {
+ if (mAhwb) {
+ AHardwareBuffer_unlock(mAhwb, nullptr);
+ AHardwareBuffer_release(mAhwb);
+ }
+}
+
+Request ExecutionContext::createRequest(const TestModel& testModel, MemoryType memoryType) {
+ CHECK(memoryType == MemoryType::ASHMEM || memoryType == MemoryType::BLOB_AHWB);
+
// Model inputs.
hidl_vec<RequestArgument> inputs(testModel.main.inputIndexes.size());
size_t inputSize = 0;
@@ -80,16 +137,19 @@
}
// Allocate memory pools.
- hidl_vec<hidl_memory> pools = {nn::allocateSharedMemory(inputSize),
- nn::allocateSharedMemory(outputSize)};
- CHECK_NE(pools[kInputPoolIndex].size(), 0u);
- CHECK_NE(pools[kOutputPoolIndex].size(), 0u);
- sp<IMemory> inputMemory = mapMemory(pools[kInputPoolIndex]);
- CHECK(inputMemory.get() != nullptr);
- uint8_t* inputPtr = static_cast<uint8_t*>(static_cast<void*>(inputMemory->getPointer()));
- CHECK(inputPtr != nullptr);
+ if (memoryType == MemoryType::ASHMEM) {
+ mInputMemory = TestAshmem::create(inputSize);
+ mOutputMemory = TestAshmem::create(outputSize);
+ } else {
+ mInputMemory = TestBlobAHWB::create(inputSize);
+ mOutputMemory = TestBlobAHWB::create(outputSize);
+ }
+ EXPECT_NE(mInputMemory, nullptr);
+ EXPECT_NE(mOutputMemory, nullptr);
+ hidl_vec<hidl_memory> pools = {mInputMemory->getHidlMemory(), mOutputMemory->getHidlMemory()};
// Copy input data to the memory pool.
+ uint8_t* inputPtr = mInputMemory->getPointer();
for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
if (op.data.size() > 0) {
@@ -102,18 +162,13 @@
return {.inputs = std::move(inputs), .outputs = std::move(outputs), .pools = std::move(pools)};
}
-std::vector<TestBuffer> getOutputBuffers(const Request& request) {
- sp<IMemory> outputMemory = mapMemory(request.pools[kOutputPoolIndex]);
- CHECK(outputMemory.get() != nullptr);
- uint8_t* outputPtr = static_cast<uint8_t*>(static_cast<void*>(outputMemory->getPointer()));
- CHECK(outputPtr != nullptr);
-
+std::vector<TestBuffer> ExecutionContext::getOutputBuffers(const Request& request) const {
// Copy out output results.
+ uint8_t* outputPtr = mOutputMemory->getPointer();
std::vector<TestBuffer> outputBuffers;
for (const auto& output : request.outputs) {
outputBuffers.emplace_back(output.location.length, outputPtr + output.location.offset);
}
-
return outputBuffers;
}
diff --git a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
index cb22250..2c17796 100644
--- a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
@@ -129,11 +129,17 @@
TEST_P(ValidationTest, Test) {
const Model model = createModel(kTestModel);
- const Request request = createRequest(kTestModel);
+ ExecutionContext context;
+ const Request request = context.createRequest(kTestModel);
ASSERT_FALSE(kTestModel.expectFailure);
validateEverything(kDevice, model, request);
}
-INSTANTIATE_GENERATED_TEST(ValidationTest, [](const test_helper::TestModel&) { return true; });
+INSTANTIATE_GENERATED_TEST(ValidationTest, [](const std::string& testName) {
+ // Skip validation for the "inputs_as_internal" and "all_tensors_as_inputs"
+ // generated tests.
+ return testName.find("inputs_as_internal") == std::string::npos &&
+ testName.find("all_tensors_as_inputs") == std::string::npos;
+});
} // namespace android::hardware::neuralnetworks::V1_0::vts::functional
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 6d4534c..3292f79 100644
--- a/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h
+++ b/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h
@@ -19,6 +19,8 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware_buffer.h>
+#include <android/hidl/memory/1.0/IMemory.h>
#include <algorithm>
#include <iosfwd>
#include <string>
@@ -28,11 +30,73 @@
namespace android::hardware::neuralnetworks {
-// Create HIDL Request from the TestModel struct.
-V1_0::Request createRequest(const test_helper::TestModel& testModel);
+// Convenience class to manage the lifetime of memory resources.
+class TestMemoryBase {
+ DISALLOW_COPY_AND_ASSIGN(TestMemoryBase);
-// After execution, copy out output results from the output memory pool.
-std::vector<::test_helper::TestBuffer> getOutputBuffers(const V1_0::Request& request);
+ public:
+ TestMemoryBase() = default;
+ virtual ~TestMemoryBase() = default;
+ uint8_t* getPointer() const { return mPtr; }
+ hidl_memory getHidlMemory() const { return mHidlMemory; }
+
+ protected:
+ uint8_t* mPtr = nullptr;
+ hidl_memory mHidlMemory;
+ bool mIsValid = false;
+};
+
+class TestAshmem : public TestMemoryBase {
+ public:
+ static std::unique_ptr<TestAshmem> create(uint32_t size);
+
+ // Prefer TestAshmem::create.
+ // The constructor calls initialize, which constructs the memory resources. This is a workaround
+ // that gtest macros cannot be used directly in a constructor.
+ TestAshmem(uint32_t size) { initialize(size); }
+
+ private:
+ void initialize(uint32_t size);
+ sp<hidl::memory::V1_0::IMemory> mMappedMemory;
+};
+
+class TestBlobAHWB : public TestMemoryBase {
+ public:
+ static std::unique_ptr<TestBlobAHWB> create(uint32_t size);
+
+ // Prefer TestBlobAHWB::create.
+ // The constructor calls initialize, which constructs the memory resources. This is a
+ // workaround that gtest macros cannot be used directly in a constructor.
+ TestBlobAHWB(uint32_t size) { initialize(size); }
+ ~TestBlobAHWB();
+
+ private:
+ void initialize(uint32_t size);
+ AHardwareBuffer* mAhwb = nullptr;
+};
+
+enum class MemoryType { ASHMEM, BLOB_AHWB, DEVICE };
+
+// Manages the lifetime of memory resources used in an execution.
+class ExecutionContext {
+ DISALLOW_COPY_AND_ASSIGN(ExecutionContext);
+
+ public:
+ static constexpr uint32_t kInputPoolIndex = 0;
+ static constexpr uint32_t kOutputPoolIndex = 1;
+
+ ExecutionContext() = default;
+
+ // Create HIDL Request from the TestModel struct.
+ V1_0::Request createRequest(const test_helper::TestModel& testModel,
+ MemoryType memoryType = MemoryType::ASHMEM);
+
+ // After execution, copy out output results from the output memory pool.
+ std::vector<test_helper::TestBuffer> getOutputBuffers(const V1_0::Request& request) const;
+
+ private:
+ std::unique_ptr<TestMemoryBase> mInputMemory, mOutputMemory;
+};
// Delete element from hidl_vec. hidl_vec doesn't support a "remove" operation,
// so this is efficiently accomplished by moving the element to the end and
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
index cee15a3..a233835 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
@@ -133,7 +133,9 @@
// Test driver for those generated from ml/nn/runtime/test/spec
void Execute(const sp<IDevice>& device, const TestModel& testModel) {
const Model model = createModel(testModel);
- const Request request = createRequest(testModel);
+
+ ExecutionContext context;
+ const Request request = context.createRequest(testModel);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
@@ -151,7 +153,7 @@
ASSERT_EQ(ErrorStatus::NONE, executionCallback->getStatus());
// Retrieve execution results.
- const std::vector<TestBuffer> outputs = getOutputBuffers(request);
+ const std::vector<TestBuffer> outputs = context.getOutputBuffers(request);
// We want "close-enough" results.
checkResults(testModel, outputs);
@@ -166,6 +168,10 @@
return TestModelManager::get().getTestModels(filter);
}
+std::vector<NamedModel> getNamedModels(const FilterNameFn& 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));
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
index cf449ea..4b1a96e 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
@@ -37,6 +37,9 @@
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
std::vector<NamedModel> getNamedModels(const FilterFn& filter);
+using FilterNameFn = std::function<bool(const std::string&)>;
+std::vector<NamedModel> getNamedModels(const FilterNameFn& filter);
+
std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
diff --git a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
index d56d40b..54e8802 100644
--- a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
@@ -132,11 +132,17 @@
TEST_P(ValidationTest, Test) {
const Model model = createModel(kTestModel);
- const Request request = createRequest(kTestModel);
+ ExecutionContext context;
+ const Request request = context.createRequest(kTestModel);
ASSERT_FALSE(kTestModel.expectFailure);
validateEverything(kDevice, model, request);
}
-INSTANTIATE_GENERATED_TEST(ValidationTest, [](const test_helper::TestModel&) { return true; });
+INSTANTIATE_GENERATED_TEST(ValidationTest, [](const std::string& testName) {
+ // Skip validation for the "inputs_as_internal" and "all_tensors_as_inputs"
+ // generated tests.
+ return testName.find("inputs_as_internal") == std::string::npos &&
+ testName.find("all_tensors_as_inputs") == std::string::npos;
+});
} // namespace android::hardware::neuralnetworks::V1_1::vts::functional
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
index 3ab0135..35275b4 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
@@ -68,6 +68,7 @@
Executor executor;
MeasureTiming measureTiming;
OutputType outputType;
+ MemoryType memoryType;
};
} // namespace
@@ -216,7 +217,8 @@
return;
}
- Request request = createRequest(testModel);
+ ExecutionContext context;
+ Request request = context.createRequest(testModel, testConfig.memoryType);
if (testConfig.outputType == OutputType::INSUFFICIENT) {
makeOutputInsufficientSize(/*outputIndex=*/0, &request);
}
@@ -326,7 +328,7 @@
}
// Retrieve execution results.
- const std::vector<TestBuffer> outputs = getOutputBuffers(request);
+ const std::vector<TestBuffer> outputs = context.getOutputBuffers(request);
// We want "close-enough" results.
checkResults(testModel, outputs);
@@ -337,24 +339,30 @@
std::vector<OutputType> outputTypesList;
std::vector<MeasureTiming> measureTimingList;
std::vector<Executor> executorList;
+ std::vector<MemoryType> memoryTypeList;
if (testDynamicOutputShape) {
outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT};
measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST};
+ memoryTypeList = {MemoryType::ASHMEM};
} else {
outputTypesList = {OutputType::FULLY_SPECIFIED};
measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST};
+ memoryTypeList = {MemoryType::ASHMEM, MemoryType::BLOB_AHWB};
}
for (const OutputType outputType : outputTypesList) {
for (const MeasureTiming measureTiming : measureTimingList) {
for (const Executor executor : executorList) {
- const TestConfig testConfig = {.executor = executor,
- .measureTiming = measureTiming,
- .outputType = outputType};
- EvaluatePreparedModel(preparedModel, testModel, testConfig);
+ for (const MemoryType memoryType : memoryTypeList) {
+ const TestConfig testConfig = {.executor = executor,
+ .measureTiming = measureTiming,
+ .outputType = outputType,
+ .memoryType = memoryType};
+ EvaluatePreparedModel(preparedModel, testModel, testConfig);
+ }
}
}
}
@@ -382,6 +390,10 @@
return TestModelManager::get().getTestModels(filter);
}
+std::vector<NamedModel> getNamedModels(const FilterNameFn& 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));
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
index dfc980c..98295ff 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
@@ -41,6 +41,9 @@
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
std::vector<NamedModel> getNamedModels(const FilterFn& filter);
+using FilterNameFn = std::function<bool(const std::string&)>;
+std::vector<NamedModel> getNamedModels(const FilterNameFn& filter);
+
std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
index 4fbd0e2..a60ec4d 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
@@ -153,7 +153,8 @@
TEST_P(ValidationTest, Test) {
const Model model = createModel(kTestModel);
- const Request request = createRequest(kTestModel);
+ ExecutionContext context;
+ const Request request = context.createRequest(kTestModel);
if (kTestModel.expectFailure) {
validateFailure(kDevice, model, request);
} else {
@@ -161,7 +162,12 @@
}
}
-INSTANTIATE_GENERATED_TEST(ValidationTest, [](const test_helper::TestModel&) { return true; });
+INSTANTIATE_GENERATED_TEST(ValidationTest, [](const std::string& testName) {
+ // Skip validation for the "inputs_as_internal" and "all_tensors_as_inputs"
+ // generated tests.
+ return testName.find("inputs_as_internal") == std::string::npos &&
+ testName.find("all_tensors_as_inputs") == std::string::npos;
+});
sp<IPreparedModel> getPreparedModel_1_2(const sp<implementation::PreparedModelCallback>& callback) {
sp<V1_0::IPreparedModel> preparedModelV1_0 = callback->getPreparedModel();
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index f936267..545a5be 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -40,6 +40,7 @@
"BasicTests.cpp",
"CompilationCachingTests.cpp",
"GeneratedTestHarness.cpp",
+ "MemoryDomainTests.cpp",
"QualityOfServiceTests.cpp",
"TestAssertions.cpp",
"ValidateBurst.cpp",
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index 5689a39..4dbac16 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -72,21 +72,10 @@
namespace {
-enum class Executor { ASYNC, SYNC, BURST, FENCED };
-
enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT, MISSED_DEADLINE };
-enum class MemoryType { SHARED, DEVICE };
-
enum class IOType { INPUT, OUTPUT };
-static void waitForSyncFence(int syncFd) {
- constexpr int kInfiniteTimeout = -1;
- ASSERT_GT(syncFd, 0);
- int r = sync_wait(syncFd, kInfiniteTimeout);
- ASSERT_GE(r, 0);
-}
-
struct TestConfig {
Executor executor;
MeasureTiming measureTiming;
@@ -277,6 +266,13 @@
} // namespace
+void waitForSyncFence(int syncFd) {
+ constexpr int kInfiniteTimeout = -1;
+ ASSERT_GT(syncFd, 0);
+ int r = sync_wait(syncFd, kInfiniteTimeout);
+ ASSERT_GE(r, 0);
+}
+
Model createModel(const TestModel& testModel) {
uint32_t constCopySize = 0;
uint32_t constRefSize = 0;
@@ -338,21 +334,39 @@
}
}
-constexpr uint32_t kInputPoolIndex = 0;
-constexpr uint32_t kOutputPoolIndex = 1;
-constexpr uint32_t kDeviceMemoryBeginIndex = 2;
+class ExecutionContextV1_3 {
+ public:
+ ExecutionContextV1_3(sp<IDevice> device, sp<IPreparedModel> preparedModel)
+ : kDevice(std::move(device)), kPreparedModel(std::move(preparedModel)) {}
-static std::pair<Request, std::vector<sp<IBuffer>>> createRequest(
- const sp<IDevice>& device, const sp<IPreparedModel>& preparedModel,
- const TestModel& testModel, bool preferDeviceMemory) {
+ std::optional<Request> createRequest(const TestModel& testModel, MemoryType memoryType);
+ std::vector<TestBuffer> getOutputBuffers(const TestModel& testModel,
+ const Request& request) const;
+
+ private:
+ // Get a TestBuffer with data copied from an IBuffer object.
+ void getBuffer(const sp<IBuffer>& buffer, size_t size, TestBuffer* testBuffer) const;
+
+ static constexpr uint32_t kInputPoolIndex = 0;
+ static constexpr uint32_t kOutputPoolIndex = 1;
+ static constexpr uint32_t kDeviceMemoryBeginIndex = 2;
+
+ const sp<IDevice> kDevice;
+ const sp<IPreparedModel> kPreparedModel;
+ std::unique_ptr<TestMemoryBase> mInputMemory, mOutputMemory;
+ std::vector<sp<IBuffer>> mBuffers;
+};
+
+std::optional<Request> ExecutionContextV1_3::createRequest(const TestModel& testModel,
+ MemoryType memoryType) {
// Memory pools are organized as:
// - 0: Input shared memory pool
// - 1: Output shared memory pool
// - [2, 2+i): Input device memories
// - [2+i, 2+i+o): Output device memories
- DeviceMemoryAllocator allocator(device, preparedModel, testModel);
- std::vector<sp<IBuffer>> buffers;
+ DeviceMemoryAllocator allocator(kDevice, kPreparedModel, testModel);
std::vector<uint32_t> tokens;
+ mBuffers.clear();
// Model inputs.
hidl_vec<RequestArgument> inputs(testModel.main.inputIndexes.size());
@@ -363,13 +377,13 @@
// Omitted input.
inputs[i] = {.hasNoValue = true};
continue;
- } else if (preferDeviceMemory) {
+ } else if (memoryType == MemoryType::DEVICE) {
SCOPED_TRACE("Input index = " + std::to_string(i));
auto [buffer, token] = allocator.allocate<IOType::INPUT>(i);
if (buffer != nullptr) {
- DataLocation loc = {.poolIndex = static_cast<uint32_t>(buffers.size() +
+ DataLocation loc = {.poolIndex = static_cast<uint32_t>(mBuffers.size() +
kDeviceMemoryBeginIndex)};
- buffers.push_back(std::move(buffer));
+ mBuffers.push_back(std::move(buffer));
tokens.push_back(token);
inputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
continue;
@@ -389,13 +403,13 @@
size_t outputSize = 0;
for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
- if (preferDeviceMemory) {
+ if (memoryType == MemoryType::DEVICE) {
SCOPED_TRACE("Output index = " + std::to_string(i));
auto [buffer, token] = allocator.allocate<IOType::OUTPUT>(i);
if (buffer != nullptr) {
- DataLocation loc = {.poolIndex = static_cast<uint32_t>(buffers.size() +
+ DataLocation loc = {.poolIndex = static_cast<uint32_t>(mBuffers.size() +
kDeviceMemoryBeginIndex)};
- buffers.push_back(std::move(buffer));
+ mBuffers.push_back(std::move(buffer));
tokens.push_back(token);
outputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
continue;
@@ -418,21 +432,29 @@
outputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
}
+ if (memoryType == MemoryType::DEVICE && mBuffers.empty()) {
+ return std::nullopt;
+ }
+
// Memory pools.
- hidl_vec<Request::MemoryPool> pools(kDeviceMemoryBeginIndex + buffers.size());
- pools[kInputPoolIndex].hidlMemory(nn::allocateSharedMemory(std::max<size_t>(inputSize, 1)));
- pools[kOutputPoolIndex].hidlMemory(nn::allocateSharedMemory(std::max<size_t>(outputSize, 1)));
- CHECK_NE(pools[kInputPoolIndex].hidlMemory().size(), 0u);
- CHECK_NE(pools[kOutputPoolIndex].hidlMemory().size(), 0u);
- for (uint32_t i = 0; i < buffers.size(); i++) {
+ hidl_vec<Request::MemoryPool> pools(kDeviceMemoryBeginIndex + mBuffers.size());
+ if (memoryType == MemoryType::BLOB_AHWB) {
+ mInputMemory = TestBlobAHWB::create(std::max<size_t>(inputSize, 1));
+ mOutputMemory = TestBlobAHWB::create(std::max<size_t>(outputSize, 1));
+ } else {
+ mInputMemory = TestAshmem::create(std::max<size_t>(inputSize, 1));
+ mOutputMemory = TestAshmem::create(std::max<size_t>(outputSize, 1));
+ }
+ EXPECT_NE(mInputMemory, nullptr);
+ EXPECT_NE(mOutputMemory, nullptr);
+ pools[kInputPoolIndex].hidlMemory(mInputMemory->getHidlMemory());
+ pools[kOutputPoolIndex].hidlMemory(mOutputMemory->getHidlMemory());
+ for (uint32_t i = 0; i < mBuffers.size(); i++) {
pools[kDeviceMemoryBeginIndex + i].token(tokens[i]);
}
// Copy input data to the input shared memory pool.
- sp<IMemory> inputMemory = mapMemory(pools[kInputPoolIndex].hidlMemory());
- CHECK(inputMemory.get() != nullptr);
- uint8_t* inputPtr = static_cast<uint8_t*>(static_cast<void*>(inputMemory->getPointer()));
- CHECK(inputPtr != nullptr);
+ uint8_t* inputPtr = mInputMemory->getPointer();
for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
if (!inputs[i].hasNoValue && inputs[i].location.poolIndex == kInputPoolIndex) {
const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
@@ -441,14 +463,38 @@
std::copy(begin, end, inputPtr + inputs[i].location.offset);
}
}
-
- Request request = {
+ return Request{
.inputs = std::move(inputs), .outputs = std::move(outputs), .pools = std::move(pools)};
- return {std::move(request), std::move(buffers)};
+}
+
+std::vector<TestBuffer> ExecutionContextV1_3::getOutputBuffers(const TestModel& testModel,
+ const Request& request) const {
+ // Copy out output results.
+ uint8_t* outputPtr = mOutputMemory->getPointer();
+ std::vector<TestBuffer> outputBuffers;
+ for (uint32_t i = 0; i < request.outputs.size(); i++) {
+ const auto& outputLoc = request.outputs[i].location;
+ if (outputLoc.poolIndex == kOutputPoolIndex) {
+ outputBuffers.emplace_back(outputLoc.length, outputPtr + outputLoc.offset);
+ } else {
+ const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
+ if (op.data.size() == 0) {
+ outputBuffers.emplace_back(0, nullptr);
+ } else {
+ SCOPED_TRACE("Output index = " + std::to_string(i));
+ const uint32_t bufferIndex = outputLoc.poolIndex - kDeviceMemoryBeginIndex;
+ TestBuffer buffer;
+ getBuffer(mBuffers[bufferIndex], op.data.size(), &buffer);
+ outputBuffers.push_back(std::move(buffer));
+ }
+ }
+ }
+ return outputBuffers;
}
// Get a TestBuffer with data copied from an IBuffer object.
-static void getBuffer(const sp<IBuffer>& buffer, size_t size, TestBuffer* testBuffer) {
+void ExecutionContextV1_3::getBuffer(const sp<IBuffer>& buffer, size_t size,
+ TestBuffer* testBuffer) const {
// IBuffer -> Shared memory.
hidl_memory tmp = nn::allocateSharedMemory(size);
const auto ret = buffer->copyTo(tmp);
@@ -464,35 +510,6 @@
*testBuffer = TestBuffer(size, outputPtr);
}
-static std::vector<TestBuffer> getOutputBuffers(const TestModel& testModel, const Request& request,
- const std::vector<sp<IBuffer>>& buffers) {
- sp<IMemory> outputMemory = mapMemory(request.pools[kOutputPoolIndex].hidlMemory());
- CHECK(outputMemory.get() != nullptr);
- uint8_t* outputPtr = static_cast<uint8_t*>(static_cast<void*>(outputMemory->getPointer()));
- CHECK(outputPtr != nullptr);
-
- // Copy out output results.
- std::vector<TestBuffer> outputBuffers;
- for (uint32_t i = 0; i < request.outputs.size(); i++) {
- const auto& outputLoc = request.outputs[i].location;
- if (outputLoc.poolIndex == kOutputPoolIndex) {
- outputBuffers.emplace_back(outputLoc.length, outputPtr + outputLoc.offset);
- } else {
- const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
- if (op.data.size() == 0) {
- outputBuffers.emplace_back();
- } else {
- SCOPED_TRACE("Output index = " + std::to_string(i));
- const uint32_t bufferIndex = outputLoc.poolIndex - kDeviceMemoryBeginIndex;
- TestBuffer buffer;
- getBuffer(buffers[bufferIndex], op.data.size(), &buffer);
- outputBuffers.push_back(std::move(buffer));
- }
- }
- }
- return outputBuffers;
-}
-
static bool hasZeroSizedOutput(const TestModel& testModel) {
return std::any_of(testModel.main.outputIndexes.begin(), testModel.main.outputIndexes.end(),
[&testModel](uint32_t index) {
@@ -543,13 +560,14 @@
return;
}
- auto [request, buffers] =
- createRequest(device, preparedModel, testModel,
- /*preferDeviceMemory=*/testConfig.memoryType == MemoryType::DEVICE);
+ ExecutionContextV1_3 context(device, preparedModel);
+ auto maybeRequest = context.createRequest(testModel, testConfig.memoryType);
// Skip if testing memory domain but no device memory has been allocated.
- if (testConfig.memoryType == MemoryType::DEVICE && buffers.empty()) {
+ if (!maybeRequest.has_value()) {
return;
}
+
+ Request request = std::move(maybeRequest.value());
if (testConfig.outputType == OutputType::INSUFFICIENT) {
makeOutputInsufficientSize(/*outputIndex=*/0, &request);
}
@@ -648,6 +666,7 @@
ASSERT_EQ(syncFenceHandle.getNativeHandle(), nullptr);
ASSERT_EQ(fencedCallback, nullptr);
executionStatus = result;
+ timing = {UINT64_MAX, UINT64_MAX};
} else if (syncFenceHandle.getNativeHandle()) {
// If a sync fence is returned, try start another run waiting for the sync fence.
ret = preparedModel->executeFenced(request, {syncFenceHandle},
@@ -744,7 +763,7 @@
}
// Retrieve execution results.
- const std::vector<TestBuffer> outputs = getOutputBuffers(testModel, request, buffers);
+ const std::vector<TestBuffer> outputs = context.getOutputBuffers(testModel, request);
// We want "close-enough" results.
checkResults(testModel, outputs);
@@ -755,29 +774,32 @@
std::vector<OutputType> outputTypesList;
std::vector<MeasureTiming> measureTimingList;
std::vector<Executor> executorList;
- MemoryType memoryType = MemoryType::SHARED;
+ std::vector<MemoryType> memoryTypeList;
switch (testKind) {
case TestKind::GENERAL: {
outputTypesList = {OutputType::FULLY_SPECIFIED};
measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST};
+ memoryTypeList = {MemoryType::ASHMEM};
} break;
case TestKind::DYNAMIC_SHAPE: {
outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT};
measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST, Executor::FENCED};
+ memoryTypeList = {MemoryType::ASHMEM};
} break;
case TestKind::MEMORY_DOMAIN: {
outputTypesList = {OutputType::FULLY_SPECIFIED};
measureTimingList = {MeasureTiming::NO};
executorList = {Executor::ASYNC, Executor::SYNC, Executor::FENCED};
- memoryType = MemoryType::DEVICE;
+ memoryTypeList = {MemoryType::BLOB_AHWB, MemoryType::DEVICE};
} break;
case TestKind::FENCED_COMPUTE: {
outputTypesList = {OutputType::FULLY_SPECIFIED};
measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
executorList = {Executor::FENCED};
+ memoryTypeList = {MemoryType::ASHMEM};
} break;
case TestKind::QUANTIZATION_COUPLING: {
LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel";
@@ -788,14 +810,17 @@
measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
// Burst does not support V1_3 loop timeout.
executorList = {Executor::ASYNC, Executor::SYNC, Executor::FENCED};
+ memoryTypeList = {MemoryType::ASHMEM};
} break;
}
for (const OutputType outputType : outputTypesList) {
for (const MeasureTiming measureTiming : measureTimingList) {
for (const Executor executor : executorList) {
- const TestConfig testConfig(executor, measureTiming, outputType, memoryType);
- EvaluatePreparedModel(device, preparedModel, testModel, testConfig);
+ for (const MemoryType memoryType : memoryTypeList) {
+ const TestConfig testConfig(executor, measureTiming, outputType, memoryType);
+ EvaluatePreparedModel(device, preparedModel, testModel, testConfig);
+ }
}
}
}
@@ -814,7 +839,7 @@
for (const OutputType outputType : outputTypesList) {
for (const MeasureTiming measureTiming : measureTimingList) {
for (const Executor executor : executorList) {
- const TestConfig testConfig(executor, measureTiming, outputType, MemoryType::SHARED,
+ const TestConfig testConfig(executor, measureTiming, outputType, MemoryType::ASHMEM,
/*reportSkipping=*/false);
bool baseSkipped = false;
EvaluatePreparedModel(device, preparedModel, testModel, testConfig, &baseSkipped);
@@ -891,6 +916,10 @@
return TestModelManager::get().getTestModels(filter);
}
+std::vector<NamedModel> getNamedModels(const FilterNameFn& 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));
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
index 834d335..4f05c48 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -41,6 +41,9 @@
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
std::vector<NamedModel> getNamedModels(const FilterFn& filter);
+using FilterNameFn = std::function<bool(const std::string&)>;
+std::vector<NamedModel> getNamedModels(const FilterNameFn& filter);
+
std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
#define INSTANTIATE_GENERATED_TEST(TestSuite, filter) \
@@ -77,6 +80,8 @@
void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& preparedModel,
const test_helper::TestModel& testModel, TestKind testKind);
+void waitForSyncFence(int syncFd);
+
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_3_GENERATED_TEST_HARNESS_H
diff --git a/neuralnetworks/1.3/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/1.3/vts/functional/MemoryDomainTests.cpp
new file mode 100644
index 0000000..3c0c885
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/MemoryDomainTests.cpp
@@ -0,0 +1,1203 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "neuralnetworks_hidl_hal_test"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
+#include "GeneratedTestHarness.h"
+#include "MemoryUtils.h"
+#include "TestHarness.h"
+#include "Utils.h"
+#include "VtsHalNeuralnetworks.h"
+
+namespace android::hardware::neuralnetworks::V1_3::vts::functional {
+
+using namespace test_helper;
+using implementation::ExecutionCallback;
+using implementation::PreparedModelCallback;
+using V1_0::RequestArgument;
+using V1_1::ExecutionPreference;
+using V1_2::Constant;
+using V1_2::MeasureTiming;
+using V1_2::OutputShape;
+using V1_2::Timing;
+
+namespace {
+
+const auto kNamedDeviceChoices = testing::ValuesIn(getNamedDevices());
+
+// A 1.3 driver is likely to support at least one of the following operand types.
+const std::vector<TestOperandType> kTestOperandTypeChoicesVector = {
+ TestOperandType::TENSOR_FLOAT32,
+ TestOperandType::TENSOR_FLOAT16,
+ TestOperandType::TENSOR_QUANT8_ASYMM,
+ TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+};
+const auto kTestOperandTypeChoices = testing::ValuesIn(kTestOperandTypeChoicesVector);
+
+bool isInChoices(TestOperandType type) {
+ return std::count(kTestOperandTypeChoicesVector.begin(), kTestOperandTypeChoicesVector.end(),
+ type) > 0;
+}
+
+bool isFloat(TestOperandType type) {
+ CHECK(isInChoices(type));
+ return type == TestOperandType::TENSOR_FLOAT32 || type == TestOperandType::TENSOR_FLOAT16;
+}
+
+// Create dummy buffers for model constants as well as inputs and outputs.
+// We only care about the size here because we will not check accuracy in validation tests.
+void createDummyData(TestModel* testModel) {
+ for (auto& operand : testModel->main.operands) {
+ if (operand.data != nullptr) continue;
+ switch (operand.lifetime) {
+ case TestOperandLifeTime::SUBGRAPH_INPUT:
+ case TestOperandLifeTime::SUBGRAPH_OUTPUT:
+ case TestOperandLifeTime::CONSTANT_COPY:
+ case TestOperandLifeTime::CONSTANT_REFERENCE: {
+ const uint32_t size = nn::nonExtensionOperandSizeOfData(
+ static_cast<OperandType>(operand.type), operand.dimensions);
+ operand.data = TestBuffer(size);
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
+TestOperand createInt32Scalar(int32_t value) {
+ return {
+ .type = TestOperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .data = TestBuffer::createFromVector<int32_t>({value}),
+ };
+}
+
+// Construct a test model with multiple CONV_2D operations with the given operand as inputs.
+// The dimensions of the filters are chosen to ensure outputs has the same dimensions as inputs.
+// We choose CONV_2D operation because it is commonly supported by most drivers.
+TestModel createConvModel(const TestOperand& operand, uint32_t numOperations) {
+ CHECK(isInChoices(operand.type));
+
+ TestOperand weight = {.type = operand.type,
+ .dimensions = {operand.dimensions[3], 3, 3, operand.dimensions[3]},
+ .numberOfConsumers = 1,
+ .scale = isFloat(operand.type) ? 0.0f : 1.0f,
+ .zeroPoint = 0,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY};
+
+ TestOperand bias = {
+ .type = isFloat(operand.type) ? operand.type : TestOperandType::TENSOR_INT32,
+ .dimensions = {operand.dimensions[3]},
+ .numberOfConsumers = 1,
+ .scale = operand.scale * weight.scale,
+ .zeroPoint = 0,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY};
+
+ TestOperand output = operand;
+ output.numberOfConsumers = 0;
+ output.lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT;
+
+ const std::vector<TestOperand> operands = {
+ operand,
+ std::move(weight),
+ std::move(bias),
+ createInt32Scalar(1), // same padding
+ createInt32Scalar(1), // width stride
+ createInt32Scalar(1), // height stride
+ createInt32Scalar(0), // activation = NONE
+ std::move(output),
+ };
+
+ TestModel model;
+ for (uint32_t i = 0; i < numOperations; i++) {
+ model.main.operands.insert(model.main.operands.end(), operands.begin(), operands.end());
+ const uint32_t inputIndex = operands.size() * i;
+ const uint32_t outputIndex = inputIndex + operands.size() - 1;
+ std::vector<uint32_t> inputs(operands.size() - 1);
+ std::iota(inputs.begin(), inputs.end(), inputIndex);
+ model.main.operations.push_back({.type = TestOperationType::CONV_2D,
+ .inputs = std::move(inputs),
+ .outputs = {outputIndex}});
+ model.main.inputIndexes.push_back(inputIndex);
+ model.main.outputIndexes.push_back(outputIndex);
+ }
+ createDummyData(&model);
+ return model;
+}
+
+// Construct a test model with a single ADD operation with the given operand as input0 and input1.
+// This is to cover additional cases that the CONV_2D model does not support, e.g. arbitrary input
+// operand rank, scalar input operand. We choose ADD operation because it is commonly supported by
+// most drivers.
+TestModel createSingleAddModel(const TestOperand& operand) {
+ CHECK(isInChoices(operand.type));
+
+ TestOperand act = {
+ .type = TestOperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ };
+
+ TestOperand output = operand;
+ output.numberOfConsumers = 0;
+ output.lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT;
+
+ TestModel model = {
+ .main =
+ {
+ .operands =
+ {
+ operand,
+ operand,
+ std::move(act),
+ output,
+ },
+ .operations = {{.type = TestOperationType::ADD,
+ .inputs = {0, 1, 2},
+ .outputs = {3}}},
+ .inputIndexes = {0, 1, 2},
+ .outputIndexes = {3},
+ },
+ };
+ createDummyData(&model);
+ return model;
+}
+
+// A dummy invalid IPreparedModel class for MemoryDomainAllocateTest.InvalidPreparedModel
+class InvalidPreparedModel : public IPreparedModel {
+ public:
+ Return<V1_0::ErrorStatus> execute(const V1_0::Request&,
+ const sp<V1_0::IExecutionCallback>&) override {
+ return V1_0::ErrorStatus::GENERAL_FAILURE;
+ }
+ Return<V1_0::ErrorStatus> execute_1_2(const V1_0::Request&, V1_2::MeasureTiming,
+ const sp<V1_2::IExecutionCallback>&) override {
+ return V1_0::ErrorStatus::GENERAL_FAILURE;
+ }
+ Return<V1_3::ErrorStatus> execute_1_3(const V1_3::Request&, V1_2::MeasureTiming,
+ const V1_3::OptionalTimePoint&,
+ const V1_3::OptionalTimeoutDuration&,
+ const sp<V1_3::IExecutionCallback>&) override {
+ return V1_3::ErrorStatus::GENERAL_FAILURE;
+ }
+ Return<void> executeSynchronously(const V1_0::Request&, V1_2::MeasureTiming,
+ executeSynchronously_cb) override {
+ return Void();
+ }
+ Return<void> executeSynchronously_1_3(const V1_3::Request&, V1_2::MeasureTiming,
+ const V1_3::OptionalTimePoint&,
+ const V1_3::OptionalTimeoutDuration&,
+ executeSynchronously_1_3_cb) override {
+ return Void();
+ }
+ Return<void> configureExecutionBurst(const sp<V1_2::IBurstCallback>&,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>&,
+ const MQDescriptorSync<V1_2::FmqResultDatum>&,
+ configureExecutionBurst_cb) override {
+ return Void();
+ }
+ Return<void> executeFenced(const V1_3::Request&, const hidl_vec<hidl_handle>&,
+ V1_2::MeasureTiming, const V1_3::OptionalTimePoint&,
+ const V1_3::OptionalTimeoutDuration&,
+ const V1_3::OptionalTimeoutDuration&, executeFenced_cb) override {
+ return Void();
+ }
+};
+
+} // namespace
+
+class MemoryDomainTestBase : public testing::Test {
+ protected:
+ MemoryDomainTestBase(sp<IDevice> device, TestOperandType type)
+ : kDevice(std::move(device)),
+ kTestOperandType(type),
+ kTestOperand(kTestOperandMap.at(type)),
+ kTestOperandDataSize(nn::nonExtensionOperandSizeOfData(static_cast<OperandType>(type),
+ kTestOperand.dimensions)) {}
+
+ void SetUp() override {
+ testing::Test::SetUp();
+ ASSERT_NE(kDevice, nullptr);
+ }
+
+ sp<IPreparedModel> createConvPreparedModel(const TestOperand& testOperand,
+ uint32_t numOperations = 1) {
+ const TestModel testModel = createConvModel(testOperand, numOperations);
+ const Model model = createModel(testModel);
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(kDevice, model, &preparedModel, /*reportSkipping=*/false);
+ return preparedModel;
+ }
+
+ sp<IPreparedModel> createAddPreparedModel(const TestOperand& testOperand) {
+ const TestModel testModel = createSingleAddModel(testOperand);
+ const Model model = createModel(testModel);
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(kDevice, model, &preparedModel, /*reportSkipping=*/false);
+ return preparedModel;
+ }
+
+ static const std::map<TestOperandType, TestOperand> kTestOperandMap;
+
+ const sp<IDevice> kDevice;
+ const TestOperandType kTestOperandType;
+ const TestOperand& kTestOperand;
+ const uint32_t kTestOperandDataSize;
+};
+
+const std::map<TestOperandType, TestOperand> MemoryDomainTestBase::kTestOperandMap = {
+ {TestOperandType::TENSOR_FLOAT32,
+ {
+ .type = TestOperandType::TENSOR_FLOAT32,
+ .dimensions = {1, 32, 32, 8},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ }},
+ {TestOperandType::TENSOR_FLOAT16,
+ {
+ .type = TestOperandType::TENSOR_FLOAT16,
+ .dimensions = {1, 32, 32, 8},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ }},
+ {TestOperandType::TENSOR_QUANT8_ASYMM,
+ {
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .dimensions = {1, 32, 32, 8},
+ .numberOfConsumers = 1,
+ .scale = 0.5f,
+ .zeroPoint = 0,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ }},
+ {TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+ {
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+ .dimensions = {1, 32, 32, 8},
+ .numberOfConsumers = 1,
+ .scale = 0.5f,
+ .zeroPoint = 0,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ }},
+};
+
+using MemoryDomainAllocateTestParam = std::tuple<NamedDevice, TestOperandType>;
+class MemoryDomainAllocateTest : public MemoryDomainTestBase,
+ public testing::WithParamInterface<MemoryDomainAllocateTestParam> {
+ protected:
+ MemoryDomainAllocateTest()
+ : MemoryDomainTestBase(getData(std::get<NamedDevice>(GetParam())),
+ std::get<TestOperandType>(GetParam())) {}
+
+ struct AllocateTestArgs {
+ hidl_vec<uint32_t> dimensions;
+ hidl_vec<sp<IPreparedModel>> preparedModels;
+ hidl_vec<BufferRole> inputRoles;
+ hidl_vec<BufferRole> outputRoles;
+ };
+
+ // Validation test for IDevice::allocate. The driver is expected to fail with INVALID_ARGUMENT,
+ // or GENERAL_FAILURE if memory domain is not supported.
+ void validateAllocate(AllocateTestArgs args) {
+ const auto ret = kDevice->allocate(
+ {.dimensions = std::move(args.dimensions)}, std::move(args.preparedModels),
+ std::move(args.inputRoles), std::move(args.outputRoles),
+ [](ErrorStatus status, const sp<IBuffer>& buffer, uint32_t token) {
+ EXPECT_TRUE(status == ErrorStatus::INVALID_ARGUMENT ||
+ status == ErrorStatus::GENERAL_FAILURE);
+ EXPECT_EQ(buffer, nullptr);
+ EXPECT_EQ(token, 0);
+ });
+ ASSERT_TRUE(ret.isOk());
+ }
+
+ void testConflictOperands(const sp<IPreparedModel>& model1, const sp<IPreparedModel>& model2) {
+ validateAllocate({
+ .preparedModels = {model1, model2},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ });
+ validateAllocate({
+ .preparedModels = {model1, model2},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ });
+ validateAllocate({
+ .preparedModels = {model1, model2},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ });
+ }
+};
+
+TEST_P(MemoryDomainAllocateTest, EmptyRole) {
+ // Test with empty prepared models and roles.
+ validateAllocate({});
+
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ // Test again with non-empty prepared models but empty roles.
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ });
+}
+
+TEST_P(MemoryDomainAllocateTest, NullptrPreparedModel) {
+ // Test with nullptr prepared model as input role.
+ validateAllocate({
+ .preparedModels = {nullptr},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+
+ // Test with nullptr prepared model as output role.
+ validateAllocate({
+ .preparedModels = {nullptr},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+}
+
+TEST_P(MemoryDomainAllocateTest, InvalidPreparedModel) {
+ sp<InvalidPreparedModel> invalidPreparedModel = new InvalidPreparedModel();
+
+ // Test with invalid prepared model as input role.
+ validateAllocate({
+ .preparedModels = {invalidPreparedModel},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+
+ // Test with invalid prepared model as output role.
+ validateAllocate({
+ .preparedModels = {invalidPreparedModel},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+}
+
+TEST_P(MemoryDomainAllocateTest, InvalidModelIndex) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ // This should fail, because the model index is out of bound.
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ .inputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ });
+
+ // This should fail, because the model index is out of bound.
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ });
+}
+
+TEST_P(MemoryDomainAllocateTest, InvalidIOIndex) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ // This should fail, because the model only has one input.
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 1, .frequency = 1.0f}},
+ });
+
+ // This should fail, because the model only has one output.
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 1, .frequency = 1.0f}},
+ });
+}
+
+TEST_P(MemoryDomainAllocateTest, InvalidFrequency) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ for (float invalidFreq : {10.0f, 0.0f, -0.5f}) {
+ // Test with invalid frequency for input roles.
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = invalidFreq}},
+ });
+ // Test with invalid frequency for output roles.
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = invalidFreq}},
+ });
+ }
+}
+
+TEST_P(MemoryDomainAllocateTest, SameRoleSpecifiedTwice) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ // Same role with same model index.
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
+ {.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+ validateAllocate({
+ .preparedModels = {preparedModel},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
+ {.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+
+ // Different model indexes, but logically referring to the same role.
+ validateAllocate({
+ .preparedModels = {preparedModel, preparedModel},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ });
+ validateAllocate({
+ .preparedModels = {preparedModel, preparedModel},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ });
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictOperandType) {
+ const std::map<TestOperandType, TestOperandType> conflictTypeMap = {
+ {TestOperandType::TENSOR_FLOAT32, TestOperandType::TENSOR_FLOAT16},
+ {TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_FLOAT32},
+ {TestOperandType::TENSOR_QUANT8_ASYMM, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+ {TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED, TestOperandType::TENSOR_QUANT8_ASYMM},
+ };
+
+ TestOperand conflictTestOperand = kTestOperand;
+ const auto it = conflictTypeMap.find(kTestOperandType);
+ ASSERT_FALSE(it == conflictTypeMap.end());
+ conflictTestOperand.type = it->second;
+
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto conflictPreparedModel = createConvPreparedModel(conflictTestOperand);
+ if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+ testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictScale) {
+ if (isFloat(kTestOperandType)) return;
+
+ TestOperand conflictTestOperand = kTestOperand;
+ ASSERT_NE(conflictTestOperand.scale, 1.0f);
+ conflictTestOperand.scale = 1.0f;
+
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto conflictPreparedModel = createConvPreparedModel(conflictTestOperand);
+ if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+ testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictZeroPoint) {
+ if (isFloat(kTestOperandType)) return;
+
+ TestOperand conflictTestOperand = kTestOperand;
+ ASSERT_NE(conflictTestOperand.zeroPoint, 10);
+ conflictTestOperand.zeroPoint = 10;
+
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto conflictPreparedModel = createConvPreparedModel(conflictTestOperand);
+ if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+ testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictRankBetweenRoles) {
+ TestOperand conflictTestOperand = kTestOperand;
+ conflictTestOperand.dimensions.pop_back();
+
+ auto preparedModel = createAddPreparedModel(kTestOperand);
+ auto conflictPreparedModel = createAddPreparedModel(conflictTestOperand);
+ if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+ testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictDimensionsBetweenRoles) {
+ TestOperand conflictTestOperand = kTestOperand;
+ conflictTestOperand.dimensions[0] = 4;
+
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto conflictPreparedModel = createConvPreparedModel(conflictTestOperand);
+ if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+ testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictRankBetweenRoleAndDesc) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ auto badDimensions = kTestOperand.dimensions;
+ badDimensions.pop_back();
+
+ validateAllocate({
+ .dimensions = badDimensions,
+ .preparedModels = {preparedModel},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+ validateAllocate({
+ .dimensions = badDimensions,
+ .preparedModels = {preparedModel},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictDimensionsBetweenRoleAndDesc) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ auto badDimensions = kTestOperand.dimensions;
+ badDimensions[0] = 4;
+
+ validateAllocate({
+ .dimensions = badDimensions,
+ .preparedModels = {preparedModel},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+ validateAllocate({
+ .dimensions = badDimensions,
+ .preparedModels = {preparedModel},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ });
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictRankWithScalarRole) {
+ auto preparedModel = createAddPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ // This should fail, because the target operand is a scalar but a non-empty dimension is
+ // specified.
+ validateAllocate({
+ .dimensions = {1},
+ .preparedModels = {preparedModel},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 2, .frequency = 1.0f}},
+ });
+}
+
+std::string printMemoryDomainAllocateTest(
+ const testing::TestParamInfo<MemoryDomainAllocateTestParam>& info) {
+ const auto& [namedDevice, operandType] = info.param;
+ const std::string type = toString(static_cast<OperandType>(operandType));
+ return gtestCompliantName(getName(namedDevice) + "_" + type);
+}
+
+INSTANTIATE_TEST_CASE_P(TestMemoryDomain, MemoryDomainAllocateTest,
+ testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices),
+ printMemoryDomainAllocateTest);
+
+class MemoryDomainCopyTestBase : public MemoryDomainTestBase {
+ protected:
+ MemoryDomainCopyTestBase(sp<IDevice> device, TestOperandType type)
+ : MemoryDomainTestBase(std::move(device), type) {}
+
+ // Allocates device memory for roles of a single prepared model.
+ // Returns {IBuffer, token} if success; returns {nullptr, 0} if not supported.
+ std::pair<sp<IBuffer>, uint32_t> allocateBuffer(const sp<IPreparedModel>& preparedModel,
+ const std::vector<uint32_t>& inputIndexes,
+ const std::vector<uint32_t>& outputIndexes,
+ const std::vector<uint32_t>& dimensions) {
+ if (preparedModel == nullptr) {
+ return {nullptr, 0};
+ }
+
+ hidl_vec<BufferRole> inputRoles(inputIndexes.size()), outputRoles(outputIndexes.size());
+ auto trans = [](uint32_t ind) -> BufferRole {
+ return {.modelIndex = 0, .ioIndex = ind, .frequency = 1.0f};
+ };
+ std::transform(inputIndexes.begin(), inputIndexes.end(), inputRoles.begin(), trans);
+ std::transform(outputIndexes.begin(), outputIndexes.end(), outputRoles.begin(), trans);
+
+ sp<IBuffer> buffer;
+ uint32_t token = 0;
+ const auto ret = kDevice->allocate(
+ {.dimensions = dimensions}, {preparedModel}, std::move(inputRoles),
+ std::move(outputRoles),
+ [&buffer, &token](ErrorStatus err, const sp<IBuffer>& buf, uint32_t tok) {
+ if (err == ErrorStatus::NONE) {
+ EXPECT_NE(buf, nullptr);
+ EXPECT_GT(tok, 0);
+ buffer = buf;
+ token = tok;
+ } else {
+ EXPECT_EQ(err, ErrorStatus::GENERAL_FAILURE);
+ EXPECT_EQ(buf, nullptr);
+ EXPECT_EQ(tok, 0);
+ }
+ });
+ EXPECT_TRUE(ret.isOk());
+ return {std::move(buffer), token};
+ }
+
+ std::pair<sp<IBuffer>, uint32_t> allocateBuffer(const sp<IPreparedModel>& preparedModel,
+ const std::vector<uint32_t>& inputIndexes,
+ const std::vector<uint32_t>& outputIndexes) {
+ return allocateBuffer(preparedModel, inputIndexes, outputIndexes, {});
+ }
+
+ hidl_memory allocateSharedMemory(uint32_t size) {
+ hidl_memory memory = nn::allocateSharedMemory(size);
+ EXPECT_EQ(memory.size(), size);
+ return memory;
+ }
+
+ void testCopyFrom(const sp<IBuffer>& buffer, const hidl_memory& memory,
+ const std::vector<uint32_t>& dimensions, ErrorStatus expectedStatus) {
+ const auto ret = buffer->copyFrom(memory, dimensions);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(static_cast<ErrorStatus>(ret), expectedStatus);
+ }
+
+ void testCopyTo(const sp<IBuffer>& buffer, const hidl_memory& memory,
+ ErrorStatus expectedStatus) {
+ const auto ret = buffer->copyTo(memory);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(static_cast<ErrorStatus>(ret), expectedStatus);
+ }
+
+ void initializeDeviceMemory(const sp<IBuffer>& buffer) {
+ hidl_memory memory = nn::allocateSharedMemory(kTestOperandDataSize);
+ ASSERT_EQ(memory.size(), kTestOperandDataSize);
+ testCopyFrom(buffer, memory, kTestOperand.dimensions, ErrorStatus::NONE);
+ }
+};
+
+using MemoryDomainCopyTestParam = std::tuple<NamedDevice, TestOperandType>;
+class MemoryDomainCopyTest : public MemoryDomainCopyTestBase,
+ public testing::WithParamInterface<MemoryDomainCopyTestParam> {
+ protected:
+ MemoryDomainCopyTest()
+ : MemoryDomainCopyTestBase(getData(std::get<NamedDevice>(GetParam())),
+ std::get<TestOperandType>(GetParam())) {}
+};
+
+TEST_P(MemoryDomainCopyTest, CopyFrom_InvalidMemorySize) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+
+ uint32_t badMemorySize1 = kTestOperandDataSize / 2, badMemorySize2 = kTestOperandDataSize * 2;
+ hidl_memory badMemory1 = allocateSharedMemory(badMemorySize1);
+ hidl_memory badMemory2 = allocateSharedMemory(badMemorySize2);
+ testCopyFrom(buffer, badMemory1, {}, ErrorStatus::INVALID_ARGUMENT);
+ testCopyFrom(buffer, badMemory2, {}, ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyFrom_InvalidMemorySize_DynamicShape) {
+ TestOperand testOperand = kTestOperand;
+ testOperand.dimensions[0] = 0;
+ auto preparedModel = createConvPreparedModel(testOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+
+ uint32_t badMemorySize1 = kTestOperandDataSize / 2, badMemorySize2 = kTestOperandDataSize * 2;
+ hidl_memory badMemory1 = allocateSharedMemory(badMemorySize1);
+ hidl_memory badMemory2 = allocateSharedMemory(badMemorySize2);
+ hidl_memory goodMemory = allocateSharedMemory(kTestOperandDataSize);
+
+ auto badDimensions = kTestOperand.dimensions;
+ badDimensions[0] = 2;
+
+ testCopyFrom(buffer, badMemory1, kTestOperand.dimensions, ErrorStatus::INVALID_ARGUMENT);
+ testCopyFrom(buffer, badMemory2, kTestOperand.dimensions, ErrorStatus::INVALID_ARGUMENT);
+ testCopyFrom(buffer, goodMemory, kTestOperand.dimensions, ErrorStatus::NONE);
+ testCopyFrom(buffer, goodMemory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyFrom_InvalidDimensions) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+
+ hidl_memory memory = allocateSharedMemory(kTestOperandDataSize);
+
+ std::vector<uint32_t> badDimensions;
+ badDimensions = kTestOperand.dimensions;
+ badDimensions.pop_back();
+ testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+ badDimensions = kTestOperand.dimensions;
+ badDimensions[0] = 2;
+ testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+ badDimensions = kTestOperand.dimensions;
+ badDimensions[0] = 0;
+ testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+ testCopyFrom(buffer, memory, {}, ErrorStatus::NONE);
+ testCopyFrom(buffer, memory, kTestOperand.dimensions, ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyFrom_InvalidDimensions_DynamicShape) {
+ TestOperand testOperand = kTestOperand;
+ testOperand.dimensions[0] = 0;
+ auto preparedModel = createConvPreparedModel(testOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+
+ hidl_memory memory = allocateSharedMemory(kTestOperandDataSize);
+
+ std::vector<uint32_t> badDimensions;
+ badDimensions = kTestOperand.dimensions;
+ badDimensions.pop_back();
+ testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+ badDimensions = kTestOperand.dimensions;
+ badDimensions[0] = 2;
+ badDimensions[3] = 4;
+ testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+ badDimensions = kTestOperand.dimensions;
+ badDimensions[0] = 1;
+ badDimensions[3] = 0;
+ testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+ testCopyFrom(buffer, memory, {}, ErrorStatus::INVALID_ARGUMENT);
+ testCopyFrom(buffer, memory, kTestOperand.dimensions, ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyTo_UninitializedMemory) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+
+ hidl_memory memory = allocateSharedMemory(kTestOperandDataSize);
+ testCopyTo(buffer, memory, ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyTo_InvalidMemorySize) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+
+ uint32_t badMemorySize1 = kTestOperandDataSize / 2, badMemorySize2 = kTestOperandDataSize * 2;
+ hidl_memory badMemory1 = allocateSharedMemory(badMemorySize1);
+ hidl_memory badMemory2 = allocateSharedMemory(badMemorySize2);
+ hidl_memory goodMemory = allocateSharedMemory(kTestOperandDataSize);
+
+ initializeDeviceMemory(buffer);
+ testCopyTo(buffer, badMemory1, ErrorStatus::INVALID_ARGUMENT);
+ testCopyTo(buffer, badMemory2, ErrorStatus::INVALID_ARGUMENT);
+ testCopyTo(buffer, goodMemory, ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyTo_InvalidMemorySize_DynamicShape) {
+ TestOperand testOperand = kTestOperand;
+ testOperand.dimensions[0] = 0;
+ auto preparedModel = createConvPreparedModel(testOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+
+ uint32_t badMemorySize1 = kTestOperandDataSize / 2, badMemorySize2 = kTestOperandDataSize * 2;
+ hidl_memory badMemory1 = allocateSharedMemory(badMemorySize1);
+ hidl_memory badMemory2 = allocateSharedMemory(badMemorySize2);
+ hidl_memory goodMemory = allocateSharedMemory(kTestOperandDataSize);
+
+ initializeDeviceMemory(buffer);
+ testCopyTo(buffer, badMemory1, ErrorStatus::INVALID_ARGUMENT);
+ testCopyTo(buffer, badMemory2, ErrorStatus::INVALID_ARGUMENT);
+ testCopyTo(buffer, goodMemory, ErrorStatus::NONE);
+}
+
+std::string printMemoryDomainCopyTest(
+ const testing::TestParamInfo<MemoryDomainCopyTestParam>& info) {
+ const auto& [namedDevice, operandType] = info.param;
+ const std::string type = toString(static_cast<OperandType>(operandType));
+ return gtestCompliantName(getName(namedDevice) + "_" + type);
+}
+
+INSTANTIATE_TEST_CASE_P(TestMemoryDomain, MemoryDomainCopyTest,
+ testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices),
+ printMemoryDomainCopyTest);
+
+using MemoryDomainExecutionTestParam = std::tuple<NamedDevice, TestOperandType, Executor>;
+class MemoryDomainExecutionTest
+ : public MemoryDomainCopyTestBase,
+ public testing::WithParamInterface<MemoryDomainExecutionTestParam> {
+ protected:
+ MemoryDomainExecutionTest()
+ : MemoryDomainCopyTestBase(getData(std::get<NamedDevice>(GetParam())),
+ std::get<TestOperandType>(GetParam())) {}
+
+ Request::MemoryPool createSharedMemoryPool(uint32_t size) {
+ hidl_memory memory = allocateSharedMemory(size);
+ Request::MemoryPool pool;
+ pool.hidlMemory(memory);
+ return pool;
+ }
+
+ Request::MemoryPool createDeviceMemoryPool(uint32_t token) {
+ Request::MemoryPool pool;
+ pool.token(token);
+ return pool;
+ }
+
+ void testExecution(const sp<IPreparedModel>& preparedModel, const Request& request,
+ ErrorStatus expectedStatus) {
+ switch (kExecutor) {
+ case Executor::ASYNC:
+ EXPECT_EQ(executeAsync(preparedModel, request), expectedStatus);
+ break;
+ case Executor::SYNC:
+ EXPECT_EQ(executeSync(preparedModel, request), expectedStatus);
+ break;
+ case Executor::FENCED:
+ EXPECT_EQ(executeFenced(preparedModel, request), expectedStatus);
+ break;
+ default:
+ ASSERT_TRUE(false);
+ }
+ }
+
+ ErrorStatus executeAsync(const sp<IPreparedModel>& preparedModel, const Request& request) {
+ ErrorStatus executionStatus;
+
+ // launch execution
+ sp<ExecutionCallback> executionCallback = new ExecutionCallback();
+ const auto ret =
+ preparedModel->execute_1_3(request, MeasureTiming::NO, {}, {}, executionCallback);
+ EXPECT_TRUE(ret.isOk());
+ executionStatus = static_cast<ErrorStatus>(ret);
+
+ // retrieve execution status
+ executionCallback->wait();
+ if (executionStatus == ErrorStatus::NONE) {
+ executionStatus = executionCallback->getStatus();
+ } else {
+ EXPECT_EQ(executionStatus, executionCallback->getStatus());
+ }
+ const auto timing = executionCallback->getTiming();
+ EXPECT_EQ(UINT64_MAX, timing.timeOnDevice);
+ EXPECT_EQ(UINT64_MAX, timing.timeInDriver);
+ if (executionStatus != ErrorStatus::NONE) {
+ EXPECT_EQ(executionCallback->getOutputShapes().size(), 0);
+ }
+ return executionStatus;
+ }
+
+ ErrorStatus executeSync(const sp<IPreparedModel>& preparedModel, const Request& request) {
+ ErrorStatus executionStatus;
+ const auto ret = preparedModel->executeSynchronously_1_3(
+ request, MeasureTiming::NO, {}, {},
+ [&executionStatus](ErrorStatus error, const hidl_vec<OutputShape>& shapes,
+ const Timing& time) {
+ executionStatus = error;
+ EXPECT_EQ(UINT64_MAX, time.timeOnDevice);
+ EXPECT_EQ(UINT64_MAX, time.timeInDriver);
+ if (executionStatus != ErrorStatus::NONE) {
+ EXPECT_EQ(shapes.size(), 0);
+ }
+ });
+ EXPECT_TRUE(ret.isOk());
+ return executionStatus;
+ }
+
+ ErrorStatus executeFenced(const sp<IPreparedModel>& preparedModel, const Request& request) {
+ ErrorStatus executionStatus;
+ hidl_handle syncFenceHandle;
+ sp<IFencedExecutionCallback> fencedCallback;
+ const auto callbackFunc = [&executionStatus, &syncFenceHandle, &fencedCallback](
+ ErrorStatus error, const hidl_handle& handle,
+ const sp<IFencedExecutionCallback>& callback) {
+ executionStatus = error;
+ syncFenceHandle = handle;
+ fencedCallback = callback;
+ };
+ Return<void> ret = preparedModel->executeFenced(request, {}, MeasureTiming::NO, {}, {}, {},
+ callbackFunc);
+ EXPECT_TRUE(ret.isOk());
+ if (executionStatus != ErrorStatus::NONE) {
+ EXPECT_EQ(syncFenceHandle.getNativeHandle(), nullptr);
+ EXPECT_EQ(fencedCallback, nullptr);
+ return executionStatus;
+ }
+ if (syncFenceHandle.getNativeHandle()) {
+ waitForSyncFence(syncFenceHandle.getNativeHandle()->data[0]);
+ }
+ EXPECT_NE(fencedCallback, nullptr);
+ ret = fencedCallback->getExecutionInfo(
+ [&executionStatus](ErrorStatus error, Timing t, Timing) {
+ executionStatus = error;
+ EXPECT_EQ(UINT64_MAX, t.timeOnDevice);
+ EXPECT_EQ(UINT64_MAX, t.timeInDriver);
+ });
+ EXPECT_TRUE(ret.isOk());
+ return executionStatus;
+ }
+
+ const Executor kExecutor = std::get<Executor>(GetParam());
+};
+
+TEST_P(MemoryDomainExecutionTest, InvalidToken) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ if (preparedModel == nullptr) return;
+
+ Request::MemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool badDeviceMemory1 = createDeviceMemoryPool(0); // Invalid token.
+ Request::MemoryPool badDeviceMemory2 = createDeviceMemoryPool(100); // Unknown token.
+ RequestArgument sharedMemoryArg = {
+ .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+
+ testExecution(preparedModel,
+ {.inputs = {deviceMemoryArg},
+ .outputs = {sharedMemoryArg},
+ .pools = {sharedMemory, badDeviceMemory1}},
+ ErrorStatus::INVALID_ARGUMENT);
+ testExecution(preparedModel,
+ {.inputs = {deviceMemoryArg},
+ .outputs = {sharedMemoryArg},
+ .pools = {sharedMemory, badDeviceMemory2}},
+ ErrorStatus::INVALID_ARGUMENT);
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg},
+ .outputs = {deviceMemoryArg},
+ .pools = {sharedMemory, badDeviceMemory1}},
+ ErrorStatus::INVALID_ARGUMENT);
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg},
+ .outputs = {deviceMemoryArg},
+ .pools = {sharedMemory, badDeviceMemory2}},
+ ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainExecutionTest, InvalidPreparedModel) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+ auto badPreparedModel = createConvPreparedModel(kTestOperand);
+ if (badPreparedModel == nullptr) return;
+
+ Request::MemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool deviceMemory = createDeviceMemoryPool(token);
+ RequestArgument sharedMemoryArg = {
+ .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+
+ // This should fail, because the buffer is not allocated for badPreparedModel.
+ initializeDeviceMemory(buffer);
+ testExecution(badPreparedModel,
+ {.inputs = {deviceMemoryArg},
+ .outputs = {sharedMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+ testExecution(badPreparedModel,
+ {.inputs = {sharedMemoryArg},
+ .outputs = {deviceMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainExecutionTest, InvalidIOIndex) {
+ auto preparedModel = createConvPreparedModel(kTestOperand, 2);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {});
+ if (buffer == nullptr) return;
+
+ Request::MemoryPool sharedMemory1 = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool sharedMemory2 = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool sharedMemory3 = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool deviceMemory = createDeviceMemoryPool(token);
+ RequestArgument sharedMemoryArg1 = {
+ .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument sharedMemoryArg2 = {
+ .location = {.poolIndex = 1, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument sharedMemoryArg3 = {
+ .location = {.poolIndex = 2, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument deviceMemoryArg = {.location = {.poolIndex = 3}};
+
+ // This should fail, because the device memory is not allocated for input 1.
+ initializeDeviceMemory(buffer);
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg1, deviceMemoryArg},
+ .outputs = {sharedMemoryArg2, sharedMemoryArg3},
+ .pools = {sharedMemory1, sharedMemory2, sharedMemory3, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+
+ // This should fail, because the device memory is not allocated for output 1.
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg1, sharedMemoryArg2},
+ .outputs = {sharedMemoryArg3, deviceMemoryArg},
+ .pools = {sharedMemory1, sharedMemory2, sharedMemory3, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainExecutionTest, InvalidIOType) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto [inputBuffer, inputToken] = allocateBuffer(preparedModel, {0}, {});
+ auto [outputBuffer, outputToken] = allocateBuffer(preparedModel, {}, {0});
+ if (inputBuffer == nullptr || outputBuffer == nullptr) return;
+
+ Request::MemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool deviceMemory = createDeviceMemoryPool(inputToken);
+ RequestArgument sharedMemoryArg = {
+ .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+
+ // This should fail, because the device memory is allocated for input but used as output.
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg},
+ .outputs = {deviceMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+
+ // This should fail, because the device memory is allocated for output but used as input.
+ deviceMemory.token(outputToken);
+ initializeDeviceMemory(outputBuffer);
+ testExecution(preparedModel,
+ {.inputs = {deviceMemoryArg},
+ .outputs = {sharedMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainExecutionTest, UninitializedMemory) {
+ auto preparedModel = createConvPreparedModel(kTestOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+ if (buffer == nullptr) return;
+
+ Request::MemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool deviceMemory = createDeviceMemoryPool(token);
+ RequestArgument sharedMemoryArg = {
+ .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+
+ // This should fail, because the device memory is not initialized.
+ testExecution(preparedModel,
+ {.inputs = {deviceMemoryArg},
+ .outputs = {sharedMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::GENERAL_FAILURE);
+
+ // This should initialize the device memory.
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg},
+ .outputs = {deviceMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::NONE);
+
+ // Test again with initialized device memory.
+ testExecution(preparedModel,
+ {.inputs = {deviceMemoryArg},
+ .outputs = {sharedMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainExecutionTest, SameRequestMultipleRoles) {
+ auto preparedModel = createConvPreparedModel(kTestOperand, 2);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0, 1}, {0, 1});
+ if (buffer == nullptr) return;
+
+ Request::MemoryPool sharedMemory1 = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool sharedMemory2 = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool deviceMemory = createDeviceMemoryPool(token);
+ RequestArgument sharedMemoryArg1 = {
+ .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument sharedMemoryArg2 = {
+ .location = {.poolIndex = 1, .offset = 0, .length = kTestOperandDataSize}};
+ RequestArgument deviceMemoryArg = {.location = {.poolIndex = 2}};
+
+ // This should fail, because the same device memory cannot be used for both input and output.
+ initializeDeviceMemory(buffer);
+ testExecution(preparedModel,
+ {.inputs = {deviceMemoryArg, sharedMemoryArg1},
+ .outputs = {deviceMemoryArg, sharedMemoryArg2},
+ .pools = {sharedMemory1, sharedMemory2, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+
+ // This should fail, because the same device memory cannot be used for multiple outputs.
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg1, sharedMemoryArg2},
+ .outputs = {deviceMemoryArg, deviceMemoryArg},
+ .pools = {sharedMemory1, sharedMemory2, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+
+ // The same device memory can be used for multiple inputs.
+ initializeDeviceMemory(buffer);
+ testExecution(preparedModel,
+ {.inputs = {deviceMemoryArg, deviceMemoryArg},
+ .outputs = {sharedMemoryArg1, sharedMemoryArg2},
+ .pools = {sharedMemory1, sharedMemory2, deviceMemory}},
+ ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainExecutionTest, InvalidDimensions) {
+ // FENCED execution does not support dynamic shape.
+ if (kExecutor == Executor::FENCED) return;
+
+ TestOperand testOperand = kTestOperand;
+ testOperand.dimensions[0] = 0;
+ auto preparedModel = createConvPreparedModel(testOperand);
+ auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0}, kTestOperand.dimensions);
+ if (buffer == nullptr) return;
+
+ Request::MemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+ Request::MemoryPool deviceMemory = createDeviceMemoryPool(token);
+ auto badDimensions = kTestOperand.dimensions;
+ badDimensions[0] = 2;
+ RequestArgument sharedMemoryArg = {
+ .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize},
+ .dimensions = badDimensions};
+ RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+ RequestArgument deviceMemoryArgWithBadDimensions = {.location = {.poolIndex = 1},
+ .dimensions = badDimensions};
+
+ initializeDeviceMemory(buffer);
+ testExecution(preparedModel,
+ {.inputs = {deviceMemoryArgWithBadDimensions},
+ .outputs = {sharedMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg},
+ .outputs = {deviceMemoryArgWithBadDimensions},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::INVALID_ARGUMENT);
+
+ testExecution(preparedModel,
+ {.inputs = {sharedMemoryArg},
+ .outputs = {deviceMemoryArg},
+ .pools = {sharedMemory, deviceMemory}},
+ ErrorStatus::GENERAL_FAILURE);
+}
+
+const auto kExecutorChoices = testing::Values(Executor::ASYNC, Executor::SYNC, Executor::FENCED);
+
+std::string printMemoryDomainExecutionTest(
+ const testing::TestParamInfo<MemoryDomainExecutionTestParam>& info) {
+ const auto& [namedDevice, operandType, executor] = info.param;
+ const std::string type = toString(static_cast<OperandType>(operandType));
+ const std::string executorStr = toString(executor);
+ return gtestCompliantName(getName(namedDevice) + "_" + type + "_" + executorStr);
+}
+
+INSTANTIATE_TEST_CASE_P(TestMemoryDomain, MemoryDomainExecutionTest,
+ testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices,
+ kExecutorChoices),
+ printMemoryDomainExecutionTest);
+
+} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
index 879989e..2ef1e8f 100644
--- a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
@@ -214,7 +214,8 @@
}
void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
- const Request& request, bool synchronous, DeadlineBoundType deadlineBound) {
+ const Request& request, const ExecutionContext& context, bool synchronous,
+ DeadlineBoundType deadlineBound) {
const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
const auto deadline = makeDeadline(deadlineBound);
@@ -261,7 +262,7 @@
// Retrieve execution results.
ASSERT_TRUE(nn::compliantWithV1_0(request));
const V1_0::Request request10 = nn::convertToV1_0(request);
- const std::vector<TestBuffer> outputs = getOutputBuffers(request10);
+ const std::vector<TestBuffer> outputs = context.getOutputBuffers(request10);
// We want "close-enough" results.
if (status == ErrorStatus::NONE) {
@@ -270,10 +271,11 @@
}
void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
- const Request& request) {
+ const Request& request, const ExecutionContext& context) {
for (bool synchronous : {false, true}) {
for (auto deadlineBound : deadlineBounds) {
- runExecutionTest(preparedModel, testModel, request, synchronous, deadlineBound);
+ runExecutionTest(preparedModel, testModel, request, context, synchronous,
+ deadlineBound);
}
}
}
@@ -291,8 +293,9 @@
if (preparedModel == nullptr) return;
// run execution tests
- const Request request = nn::convertToV1_3(createRequest(testModel));
- runExecutionTests(preparedModel, testModel, request);
+ ExecutionContext context;
+ const Request request = nn::convertToV1_3(context.createRequest(testModel));
+ runExecutionTests(preparedModel, testModel, request, context);
}
class DeadlineTest : public GeneratedTestBase {};
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index 5b07034..df1e453 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -177,7 +177,8 @@
TEST_P(ValidationTest, Test) {
const Model model = createModel(kTestModel);
- const Request request = nn::convertToV1_3(createRequest(kTestModel));
+ ExecutionContext context;
+ const Request request = nn::convertToV1_3(context.createRequest(kTestModel));
if (kTestModel.expectFailure) {
validateFailure(kDevice, model, request);
} else {
@@ -185,11 +186,31 @@
}
}
-INSTANTIATE_GENERATED_TEST(ValidationTest, [](const test_helper::TestModel&) { return true; });
+INSTANTIATE_GENERATED_TEST(ValidationTest, [](const std::string& testName) {
+ // Skip validation for the "inputs_as_internal" and "all_tensors_as_inputs"
+ // generated tests.
+ return testName.find("inputs_as_internal") == std::string::npos &&
+ testName.find("all_tensors_as_inputs") == std::string::npos;
+});
sp<IPreparedModel> getPreparedModel_1_3(const sp<PreparedModelCallback>& callback) {
sp<V1_0::IPreparedModel> preparedModelV1_0 = callback->getPreparedModel();
return IPreparedModel::castFrom(preparedModelV1_0).withDefault(nullptr);
}
+std::string toString(Executor executor) {
+ switch (executor) {
+ case Executor::ASYNC:
+ return "ASYNC";
+ case Executor::SYNC:
+ return "SYNC";
+ case Executor::BURST:
+ return "BURST";
+ case Executor::FENCED:
+ return "FENCED";
+ default:
+ CHECK(false);
+ }
+}
+
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
index 4e51052..de082c3 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
@@ -52,6 +52,10 @@
// Utility function to get PreparedModel from callback and downcast to V1_2.
sp<IPreparedModel> getPreparedModel_1_3(const sp<implementation::PreparedModelCallback>& callback);
+enum class Executor { ASYNC, SYNC, BURST, FENCED };
+
+std::string toString(Executor executor);
+
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_3_VTS_HAL_NEURALNETWORKS_H