Merge "Remove asan variants of tests" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 63d3aed..14b9083 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -29,6 +29,23 @@
}
]
},
+ // TODO(b/244359503): Re-enable once the conversion layer is fixed.
+ // {
+ // "name": "NeuralNetworksTest_v2_static",
+ // "options": [
+ // {
+ // // Restrict NeuralNetworksTest_v2_static to run only a single
+ // // pass consisting of:
+ // // * useCpuOnly = 0
+ // // * computeMode = ComputeMode::ASYNC
+ // //
+ // // The value here is a bitmask indicating only "pass 2"
+ // // should be run (4 = 2^2). The bit conversions can be
+ // // found in packages/modules/NeuralNetworks/runtime/test/TestMain.cpp.
+ // "native-test-flag": "4"
+ // }
+ // ]
+ // },
{
"name": "CtsNNAPITestCases"
}
diff --git a/common/cpu_operations/LSTM.cpp b/common/cpu_operations/LSTM.cpp
index 8c1a4c2..66b6bee 100644
--- a/common/cpu_operations/LSTM.cpp
+++ b/common/cpu_operations/LSTM.cpp
@@ -18,11 +18,6 @@
#include "LSTM.h"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-parameter"
-#include <tensorflow/lite/kernels/internal/reference/portable_tensor_utils.h>
-#pragma clang diagnostic pop
-
#include <tensorflow/lite/kernels/internal/tensor_utils.h>
#include <vector>
diff --git a/common/types/include/nnapi/Result.h b/common/types/include/nnapi/Result.h
index 698ab70..2d27106 100644
--- a/common/types/include/nnapi/Result.h
+++ b/common/types/include/nnapi/Result.h
@@ -135,8 +135,8 @@
* following functions for the type:
* * `::android::nn::nnTryHasValue` returns `true` if the `expr` holds a successful value, false if
* the `expr` value holds an error
- * * `::android::nn::nnTryGetError` returns the successful value of `expr` or crashes
- * * `::android::nn::nnTryGetValue` returns the error value of `expr` or crashes
+ * * `::android::nn::nnTryGetError` returns the error value of `expr` or crashes
+ * * `::android::nn::nnTryGetValue` returns the successful value of `expr` or crashes
*
* Usage at call site:
* const auto [a, b, c] = NN_TRY(failableFunction(args));
diff --git a/common/types/include/nnapi/TypeUtils.h b/common/types/include/nnapi/TypeUtils.h
index 87e7c3f..b6964fc 100644
--- a/common/types/include/nnapi/TypeUtils.h
+++ b/common/types/include/nnapi/TypeUtils.h
@@ -289,6 +289,14 @@
return result;
}
+ // This is needed because conversion to Result<int> is ambiguous
+ // due to the above bool() operator overload
+ operator Result<int>() { // NOLINT(google-explicit-constructor)
+ auto result = base::unexpected(std::move(mBuffer)->str());
+ mBuffer.reset();
+ return result;
+ }
+
private:
std::optional<std::ostringstream> mBuffer = std::ostringstream{};
};
diff --git a/runtime/Android.bp b/runtime/Android.bp
index e1a9b5d..21ec0b9 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -170,6 +170,35 @@
],
}
+cc_defaults {
+ name: "libneuralnetworks_v2_defaults",
+ defaults: ["libneuralnetworks_defaults"],
+ srcs: [
+ "FlatbufferModelBuilder.cpp",
+ "NeuralNetworksV2.cpp",
+ "operation_converters/AddOperationConverter.cpp",
+ "operation_converters/ArithmeticOperationConverter.cpp",
+ "operation_converters/Conv2DOperationConverter.cpp",
+ "operation_converters/DepthwiseConv2DOperationConverter.cpp",
+ "operation_converters/LogisticOperationConverter.cpp",
+ "operation_converters/OperationConverterResolver.cpp",
+ "operation_converters/SubGraphContext.cpp",
+ ],
+
+ exclude_srcs: [
+ "NeuralNetworks.cpp",
+ ],
+
+ static_libs: [
+ "libtflite_static",
+ ],
+
+ include_dirs: [
+ "external/flatbuffers/include",
+ "external/tensorflow",
+ ],
+}
+
cc_library_shared {
name: "libneuralnetworks",
llndk: {
@@ -225,6 +254,24 @@
}
cc_library_static {
+ name: "libneuralnetworks_v2_static_experimental",
+ defaults: [
+ "libneuralnetworks_v2_defaults",
+ "neuralnetworks_defaults",
+ ],
+ exclude_static_libs: [
+ "libneuralnetworks_common",
+ "neuralnetworks_types",
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "libneuralnetworks_common_experimental",
+ "neuralnetworks_types_experimental",
+ ],
+ cflags: ["-DNN_EXPERIMENTAL_FEATURE"],
+}
+
+cc_library_static {
name: "libneuralnetworks_cl",
defaults: [
"neuralnetworks_cl_defaults",
diff --git a/runtime/FlatbufferModelBuilder.cpp b/runtime/FlatbufferModelBuilder.cpp
new file mode 100644
index 0000000..be3faaa
--- /dev/null
+++ b/runtime/FlatbufferModelBuilder.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 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 "FlatbufferModelBuilder"
+
+#include "FlatbufferModelBuilder.h"
+
+#include <LegacyUtils.h>
+
+#include "FlatbufferModelBuilderUtils.h"
+#include "operation_converters/OperationConverterResolver.h"
+
+namespace android {
+namespace nn {
+
+void FlatbufferModelBuilder::verifyModel(const tflite::Model* model) {
+ flatbuffers::Verifier verifier(mBuilder.GetBufferPointer(), mBuilder.GetSize());
+ CHECK(model != nullptr);
+ CHECK(model->Verify(verifier));
+}
+
+void FlatbufferModelBuilder::initializeBufferVector() {
+ mBufferVector.clear();
+
+ std::vector<uint8_t> emptyData;
+ auto emptyBuffer = tflite::CreateBufferDirect(mBuilder, &emptyData);
+ mBufferVector.push_back(emptyBuffer);
+}
+
+void FlatbufferModelBuilder::initializeOpCodeIndexForOperationType() {
+ mOpCodeIndexForOperationType.clear();
+ mOpCodeIndexForOperationType.resize(kNumberOfOperationTypes, -1);
+}
+
+std::vector<MetadataFlatbuffer> FlatbufferModelBuilder::createMetadataVector() {
+ std::vector<MetadataFlatbuffer> metadataVector;
+ for (uint32_t i = 0; i < mBufferVector.size(); i++) {
+ auto metadata = tflite::CreateMetadataDirect(mBuilder, std::to_string(i).c_str() /* name */,
+ i /* buffer */);
+ metadataVector.push_back(metadata);
+ }
+ return metadataVector;
+}
+
+Result<const tflite::Model*> FlatbufferModelBuilder::createTfliteModel() {
+ mModel = makeModel();
+
+ // Initialize and clear data structures
+ initializeBufferVector();
+ mOpCodesVector.clear();
+ initializeOpCodeIndexForOperationType();
+
+ // Generate subgraphs
+ auto subgraphsVector = NN_TRY(createSubGraphs());
+
+ auto metadataVector = createMetadataVector();
+
+ ModelFlatbuffer flatbufferModel = tflite::CreateModelDirect(
+ mBuilder, 3 /* version*/, &mOpCodesVector /* operator_codes */,
+ &subgraphsVector /* subgraphs */, nullptr /* description */,
+ &mBufferVector /* buffers */, nullptr /* metadata_buffer */,
+ &metadataVector /* metadata */);
+ mBuilder.Finish(flatbufferModel);
+
+ const tflite::Model* tfliteModel = tflite::GetModel(mBuilder.GetBufferPointer());
+ verifyModel(tfliteModel);
+ return tfliteModel;
+}
+
+Result<SubGraphFlatbuffer> FlatbufferModelBuilder::createSubGraphFlatbuffer(
+ const Model::Subgraph& subgraph) {
+ // TFLite does not support unspecified ranks in Operands
+ NN_TRY(checkAllTensorOperandsHaveSpecifiedRank(subgraph.operands));
+ // TFLite does not support dynamic shapes for subgrah output Operands
+ NN_TRY(checkNoSubgraphOutputOperandsHaveDynamicShape(subgraph.operands));
+
+ SubGraphContext context(&mModel, &subgraph, &mBuilder, &mOpCodesVector,
+ &mOpCodeIndexForOperationType, &mBufferVector);
+ for (const Operation& operation : subgraph.operations) {
+ const IOperationConverter* converter =
+ OperationConverterResolver::get()->findOperationConverter(operation.type);
+ NN_RET_CHECK(converter != nullptr)
+ << "IOperationConverter not implemented for OperationType: " << operation.type;
+
+ NN_TRY(converter->convert(operation, &context));
+ }
+
+ for (uint32_t idx : subgraph.inputIndexes) {
+ context.addSubGraphInput(idx);
+ }
+ for (uint32_t idx : subgraph.outputIndexes) {
+ context.addSubGraphOutput(idx);
+ }
+
+ return context.finish();
+}
+
+Result<std::vector<SubGraphFlatbuffer>> FlatbufferModelBuilder::createSubGraphs() {
+ // We do not support control flow yet
+ NN_RET_CHECK(mModel.referenced.empty()) << "Control flow for multiple subgraphs not supported";
+
+ std::vector<SubGraphFlatbuffer> subGraphVector;
+
+ auto mainSubGraph = NN_TRY(createSubGraphFlatbuffer(mModel.main));
+ subGraphVector.push_back(mainSubGraph);
+
+ return subGraphVector;
+}
+
+} // namespace nn
+} // namespace android
diff --git a/runtime/FlatbufferModelBuilder.h b/runtime/FlatbufferModelBuilder.h
new file mode 100644
index 0000000..2356cc6
--- /dev/null
+++ b/runtime/FlatbufferModelBuilder.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_FLATBUFFER_MODEL_BUILDER_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_FLATBUFFER_MODEL_BUILDER_H
+
+#include <tensorflow/lite/schema/schema_generated.h>
+
+#include <utility>
+#include <vector>
+
+#include "FlatbufferModelBuilderUtils.h"
+#include "ModelBuilder.h"
+#include "NeuralNetworks.h"
+
+namespace android {
+namespace nn {
+
+class FlatbufferModelBuilder : public ModelBuilder {
+ public:
+ // Return generated TFLite Model if successful
+ Result<const tflite::Model*> createTfliteModel();
+
+ private:
+ void verifyModel(const tflite::Model* model);
+
+ // Clears mBufferVector and initializes the first Buffer to be an empty Buffer
+ // for Tensors that do not have a buffer.
+ void initializeBufferVector();
+ // Clears mOpCodeIndexForOperationType and initializes elements to be -1
+ void initializeOpCodeIndexForOperationType();
+
+ // Helper functions to convert Subgraphs
+ Result<SubGraphFlatbuffer> createSubGraphFlatbuffer(const Model::Subgraph& subgraph);
+ Result<std::vector<SubGraphFlatbuffer>> createSubGraphs();
+
+ // Generates metadata for each Buffer
+ // Must be called after mBufferVector is filled.
+ std::vector<MetadataFlatbuffer> createMetadataVector();
+
+ flatbuffers::FlatBufferBuilder mBuilder;
+ Model mModel;
+
+ std::vector<OperatorCodeFlatbuffer> mOpCodesVector;
+ std::vector<int> mOpCodeIndexForOperationType;
+ std::vector<BufferFlatbuffer> mBufferVector;
+};
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_FLATBUFFER_MODEL_BUILDER_H
diff --git a/runtime/FlatbufferModelBuilderUtils.h b/runtime/FlatbufferModelBuilderUtils.h
new file mode 100644
index 0000000..106d931
--- /dev/null
+++ b/runtime/FlatbufferModelBuilderUtils.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_FLATBUFFER_MODEL_BUILDER_UTILS_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_FLATBUFFER_MODEL_BUILDER_UTILS_H
+
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <tensorflow/lite/schema/schema_generated.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "NeuralNetworks.h"
+#include "TypeManager.h"
+
+namespace android {
+namespace nn {
+
+using SubGraphFlatbuffer = flatbuffers::Offset<tflite::SubGraph>;
+using SubGraphsFlatbuffer = flatbuffers::Offset<flatbuffers::Vector<SubGraphFlatbuffer>>;
+
+using OperatorCodeFlatbuffer = flatbuffers::Offset<tflite::OperatorCode>;
+using OperatorFlatbuffer = flatbuffers::Offset<tflite::Operator>;
+using OperatorsFlatbuffer = flatbuffers::Offset<flatbuffers::Vector<OperatorFlatbuffer>>;
+
+using TensorFlatbuffer = flatbuffers::Offset<tflite::Tensor>;
+using TensorsFlatbuffer = flatbuffers::Offset<flatbuffers::Vector<TensorFlatbuffer>>;
+
+using BufferFlatbuffer = flatbuffers::Offset<tflite::Buffer>;
+
+using MetadataFlatbuffer = flatbuffers::Offset<tflite::Metadata>;
+
+using ModelFlatbuffer = flatbuffers::Offset<tflite::Model>;
+
+// Only supports tensor types
+// Will crash if passed in a scalar type
+inline Result<tflite::TensorType> getTensorFlatbufferOperandType(const OperandType& type) {
+ CHECK(TypeManager::get()->isTensorType(type));
+
+ // TODO: Map more operands
+ switch (type) {
+ case OperandType::TENSOR_FLOAT32:
+ return tflite::TensorType::TensorType_FLOAT32;
+ case OperandType::TENSOR_INT32:
+ return tflite::TensorType::TensorType_INT32;
+ case OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
+ return tflite::TensorType::TensorType_INT8;
+ default:
+ NN_RET_CHECK_FAIL() << "OperandType not supported: " << type;
+ }
+}
+
+inline tflite::BuiltinOperator getFlatbufferOperator(const OperationType& type) {
+ // TODO: Add more operation types
+ switch (type) {
+ case OperationType::PAD:
+ return tflite::BuiltinOperator::BuiltinOperator_PAD;
+ case OperationType::CONV_2D:
+ return tflite::BuiltinOperator::BuiltinOperator_CONV_2D;
+ case OperationType::ADD:
+ return tflite::BuiltinOperator::BuiltinOperator_ADD;
+ case OperationType::DEPTHWISE_CONV_2D:
+ return tflite::BuiltinOperator::BuiltinOperator_DEPTHWISE_CONV_2D;
+ case OperationType::LOGISTIC:
+ return tflite::BuiltinOperator::BuiltinOperator_LOGISTIC;
+ default:
+ LOG(FATAL) << "OperationType not supported: " << type;
+ return {};
+ }
+}
+
+// Referenced from external/tensorflow/tensorflow/lite/tools/versioning/op_version.cc
+inline int32_t getMaxOperatorVersionCode(tflite::BuiltinOperator builtinCode) {
+ // TODO: Add more builtin_codes
+ switch (builtinCode) {
+ case tflite::BuiltinOperator::BuiltinOperator_CONV_2D:
+ return 5;
+ case tflite::BuiltinOperator::BuiltinOperator_DEPTHWISE_CONV_2D:
+ return 6;
+ case tflite::BuiltinOperator::BuiltinOperator_ADD:
+ return 4;
+ case tflite::BuiltinOperator::BuiltinOperator_PAD:
+ return 4;
+ case tflite::BuiltinOperator::BuiltinOperator_LOGISTIC:
+ return 3;
+ default:
+ LOG(FATAL) << "BuiltinOperator not supported: " << builtinCode;
+ return {};
+ }
+}
+
+inline Result<tflite::ActivationFunctionType> getTfliteActivation(FusedActivationFunc activation) {
+ switch (activation) {
+ case FusedActivationFunc::NONE:
+ return tflite::ActivationFunctionType::ActivationFunctionType_NONE;
+ case FusedActivationFunc::RELU:
+ return tflite::ActivationFunctionType::ActivationFunctionType_RELU;
+ case FusedActivationFunc::RELU1:
+ return tflite::ActivationFunctionType::ActivationFunctionType_RELU_N1_TO_1;
+ case FusedActivationFunc::RELU6:
+ return tflite::ActivationFunctionType::ActivationFunctionType_RELU6;
+ default:
+ NN_RET_CHECK_FAIL() << "FusedActivationFunc not supported: " << activation;
+ }
+}
+
+inline bool tensorOperandHasUnspecifiedRank(const Operand& operand) {
+ return TypeManager::get()->isTensorType(operand.type) && operand.dimensions.empty();
+}
+
+inline Result<void> checkAllTensorOperandsHaveSpecifiedRank(const std::vector<Operand>& operands) {
+ NN_RET_CHECK(std::none_of(operands.begin(), operands.end(), &tensorOperandHasUnspecifiedRank))
+ << "At least one Operand has unspecified rank";
+ return {};
+}
+
+inline bool subgraphOutputOperandHasDynamicShape(const Operand& operand) {
+ return operand.lifetime == Operand::LifeTime::SUBGRAPH_OUTPUT &&
+ std::any_of(operand.dimensions.begin(), operand.dimensions.end(),
+ [](const uint32_t& dim) { return dim == 0; });
+}
+
+inline Result<void> checkNoSubgraphOutputOperandsHaveDynamicShape(
+ const std::vector<Operand>& operands) {
+ NN_RET_CHECK(
+ std::none_of(operands.begin(), operands.end(), &subgraphOutputOperandHasDynamicShape))
+ << "At least one subgraph output Operand has dynamic shape";
+ return {};
+}
+
+inline bool isOperandConstant(const Operand& operand) {
+ return operand.lifetime == Operand::LifeTime::CONSTANT_COPY ||
+ operand.lifetime == Operand::LifeTime::CONSTANT_REFERENCE;
+}
+
+inline tflite::Padding getTFLitePadding(int32_t paddingType) {
+ switch (paddingType) {
+ case ANEURALNETWORKS_PADDING_VALID: // VALID
+ case 0:
+ return tflite::Padding::Padding_VALID;
+ case ANEURALNETWORKS_PADDING_SAME: // SAME
+ return tflite::Padding::Padding_SAME;
+ default:
+ LOG(FATAL) << "Unsupported NNAPI NDK padding type: " << paddingType;
+ return {};
+ }
+}
+
+// Replace all 0 dimensions to -1 since TFLite only supports -1 as an unknown dimension
+inline void replaceZeroDimensions(std::vector<int32_t>* dims) {
+ std::replace(dims->begin(), dims->end(), 0, -1);
+}
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_FLATBUFFER_MODEL_BUILDER_UTILS_H
diff --git a/runtime/NeuralNetworksV2.cpp b/runtime/NeuralNetworksV2.cpp
new file mode 100644
index 0000000..2104994
--- /dev/null
+++ b/runtime/NeuralNetworksV2.cpp
@@ -0,0 +1,1674 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// Contains all the entry points to the C Neural Networks API.
+// We do basic validation of the operands and then call the class
+// that implements the functionality.
+
+#define LOG_TAG "NeuralNetworks"
+
+#include <ControlFlow.h>
+#include <LegacyUtils.h>
+#include <MetaModel.h>
+#include <Tracing.h>
+#include <nnapi/Types.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "BurstBuilder.h"
+#include "CompilationBuilder.h"
+#include "Event.h"
+#include "ExecutionBuilder.h"
+#include "ExecutionCallback.h"
+#include "FlatbufferModelBuilder.h"
+#include "Manager.h"
+#include "Memory.h"
+#include "NeuralNetworks.h"
+#include "NeuralNetworksExtensions.h"
+#include "NeuralNetworksOEM.h"
+#include "Telemetry.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#include "tensorflow/lite/interpreter.h"
+#include "tensorflow/lite/kernels/register.h"
+#include "tensorflow/lite/model.h"
+#pragma clang diagnostic pop
+
+using namespace android::nn;
+
+// Make sure the constants defined in the header files have not changed values.
+// IMPORTANT: When adding new values, update kNumberOfDataTypes or kNumberOfDataTypesOEM
+// in Utils.h.
+static_assert(ANEURALNETWORKS_FLOAT32 == 0, "ANEURALNETWORKS_FLOAT32 has changed");
+static_assert(ANEURALNETWORKS_INT32 == 1, "ANEURALNETWORKS_INT32 has changed");
+static_assert(ANEURALNETWORKS_UINT32 == 2, "ANEURALNETWORKS_UINT32 has changed");
+static_assert(ANEURALNETWORKS_TENSOR_FLOAT32 == 3, "ANEURALNETWORKS_TENSOR_FLOAT32 has changed");
+static_assert(ANEURALNETWORKS_TENSOR_INT32 == 4, "ANEURALNETWORKS_TENSOR_INT32 has changed");
+static_assert(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM == 5,
+ "ANEURALNETWORKS_TENSOR_QUANT8_ASYMM has changed");
+static_assert(ANEURALNETWORKS_BOOL == 6, "ANEURALNETWORKS_BOOL has changed");
+static_assert(ANEURALNETWORKS_TENSOR_QUANT16_SYMM == 7,
+ "ANEURALNETWORKS_TENSOR_QUANT16_SYMM has changed");
+static_assert(ANEURALNETWORKS_TENSOR_FLOAT16 == 8, "ANEURALNETWORKS_TENSOR_FLOAT16 has changed");
+static_assert(ANEURALNETWORKS_TENSOR_BOOL8 == 9, "ANEURALNETWORKS_TENSOR_BOOL8 has changed");
+static_assert(ANEURALNETWORKS_FLOAT16 == 10, "ANEURALNETWORKS_FLOAT16 has changed");
+static_assert(ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL == 11,
+ "ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL has changed");
+static_assert(ANEURALNETWORKS_TENSOR_QUANT16_ASYMM == 12,
+ "ANEURALNETWORKS_TENSOR_QUANT16_ASYMM has changed");
+static_assert(ANEURALNETWORKS_TENSOR_QUANT8_SYMM == 13,
+ "ANEURALNETWORKS_TENSOR_QUANT8_SYMM has changed");
+static_assert(ANEURALNETWORKS_OEM_SCALAR == 10000, "ANEURALNETWORKS_OEM_SCALAR has changed");
+static_assert(ANEURALNETWORKS_TENSOR_OEM_BYTE == 10001,
+ "ANEURALNETWORKS_TENSOR_OEM_BYTE has changed");
+
+// IMPORTANT: When adding new values, update kNumberOfOperationTypes or
+// kNumberOfOperationTypesOEMin Utils.h.
+static_assert(ANEURALNETWORKS_ADD == 0, "ANEURALNETWORKS_ADD has changed");
+static_assert(ANEURALNETWORKS_AVERAGE_POOL_2D == 1, "ANEURALNETWORKS_AVERAGE_POOL_2D has changed");
+static_assert(ANEURALNETWORKS_CONCATENATION == 2, "ANEURALNETWORKS_CONCATENATION has changed");
+static_assert(ANEURALNETWORKS_CONV_2D == 3, "ANEURALNETWORKS_CONV_2D has changed");
+static_assert(ANEURALNETWORKS_DEPTHWISE_CONV_2D == 4,
+ "ANEURALNETWORKS_DEPTHWISE_CONV_2D has changed");
+static_assert(ANEURALNETWORKS_DEPTH_TO_SPACE == 5, "ANEURALNETWORKS_DEPTH_TO_SPACE has changed");
+static_assert(ANEURALNETWORKS_DEQUANTIZE == 6, "ANEURALNETWORKS_DEQUANTIZE has changed");
+static_assert(ANEURALNETWORKS_EMBEDDING_LOOKUP == 7,
+ "ANEURALNETWORKS_EMBEDDING_LOOKUP has changed");
+static_assert(ANEURALNETWORKS_FLOOR == 8, "ANEURALNETWORKS_FLOOR has changed");
+static_assert(ANEURALNETWORKS_FULLY_CONNECTED == 9, "ANEURALNETWORKS_FULLY_CONNECTED has changed");
+static_assert(ANEURALNETWORKS_HASHTABLE_LOOKUP == 10,
+ "ANEURALNETWORKS_HASHTABLE_LOOKUP has changed");
+static_assert(ANEURALNETWORKS_L2_NORMALIZATION == 11,
+ "ANEURALNETWORKS_L2_NORMALIZATION has changed");
+static_assert(ANEURALNETWORKS_L2_POOL_2D == 12, "ANEURALNETWORKS_L2_POOL has changed");
+static_assert(ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION == 13,
+ "ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION has changed");
+static_assert(ANEURALNETWORKS_LOGISTIC == 14, "ANEURALNETWORKS_LOGISTIC has changed");
+static_assert(ANEURALNETWORKS_LSH_PROJECTION == 15, "ANEURALNETWORKS_LSH_PROJECTION has changed");
+static_assert(ANEURALNETWORKS_LSTM == 16, "ANEURALNETWORKS_LSTM has changed");
+static_assert(ANEURALNETWORKS_MAX_POOL_2D == 17, "ANEURALNETWORKS_MAX_POOL has changed");
+static_assert(ANEURALNETWORKS_MUL == 18, "ANEURALNETWORKS_MUL has changed");
+static_assert(ANEURALNETWORKS_RELU == 19, "ANEURALNETWORKS_RELU has changed");
+static_assert(ANEURALNETWORKS_RELU1 == 20, "ANEURALNETWORKS_RELU1 has changed");
+static_assert(ANEURALNETWORKS_RELU6 == 21, "ANEURALNETWORKS_RELU6 has changed");
+static_assert(ANEURALNETWORKS_RESHAPE == 22, "ANEURALNETWORKS_RESHAPE has changed");
+static_assert(ANEURALNETWORKS_RESIZE_BILINEAR == 23, "ANEURALNETWORKS_RESIZE_BILINEAR has changed");
+static_assert(ANEURALNETWORKS_RNN == 24, "ANEURALNETWORKS_RNN has changed");
+static_assert(ANEURALNETWORKS_SOFTMAX == 25, "ANEURALNETWORKS_SOFTMAX has changed");
+static_assert(ANEURALNETWORKS_SPACE_TO_DEPTH == 26, "ANEURALNETWORKS_SPACE_TO_DEPTH has changed");
+static_assert(ANEURALNETWORKS_SVDF == 27, "ANEURALNETWORKS_SVDF has changed");
+static_assert(ANEURALNETWORKS_TANH == 28, "ANEURALNETWORKS_TANH has changed");
+
+static_assert(ANEURALNETWORKS_BATCH_TO_SPACE_ND == 29,
+ "ANEURALNETWORKS_BATCH_TO_SPACE_ND has changed");
+static_assert(ANEURALNETWORKS_DIV == 30, "ANEURALNETWORKS_DIV has changed");
+static_assert(ANEURALNETWORKS_MEAN == 31, "ANEURALNETWORKS_MEAN has changed");
+static_assert(ANEURALNETWORKS_PAD == 32, "ANEURALNETWORKS_PAD has changed");
+static_assert(ANEURALNETWORKS_SPACE_TO_BATCH_ND == 33,
+ "ANEURALNETWORKS_SPACE_TO_BATCH_ND has changed");
+static_assert(ANEURALNETWORKS_SQUEEZE == 34, "ANEURALNETWORKS_SQUEEZE has changed");
+static_assert(ANEURALNETWORKS_STRIDED_SLICE == 35, "ANEURALNETWORKS_STRIDED_SLICE has changed");
+static_assert(ANEURALNETWORKS_SUB == 36, "ANEURALNETWORKS_TANH has changed");
+static_assert(ANEURALNETWORKS_TRANSPOSE == 37, "ANEURALNETWORKS_TRANSPOSE has changed");
+
+static_assert(ANEURALNETWORKS_ABS == 38, "ANEURALNETWORKS_ABS has changed");
+static_assert(ANEURALNETWORKS_ARGMAX == 39, "ANEURALNETWORKS_ARGMAX has changed");
+static_assert(ANEURALNETWORKS_ARGMIN == 40, "ANEURALNETWORKS_ARGMIN has changed");
+static_assert(ANEURALNETWORKS_AXIS_ALIGNED_BBOX_TRANSFORM == 41,
+ "ANEURALNETWORKS_AXIS_ALIGNED_BBOX_TRANSFORM has changed");
+static_assert(ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_LSTM == 42,
+ "ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_LSTM has changed");
+static_assert(ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_RNN == 43,
+ "ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_RNN has changed");
+static_assert(ANEURALNETWORKS_BOX_WITH_NMS_LIMIT == 44,
+ "ANEURALNETWORKS_BOX_WITH_NMS_LIMIT has changed");
+static_assert(ANEURALNETWORKS_CAST == 45, "ANEURALNETWORKS_CAST has changed");
+static_assert(ANEURALNETWORKS_CHANNEL_SHUFFLE == 46, "ANEURALNETWORKS_CHANNEL_SHUFFLE has changed");
+static_assert(ANEURALNETWORKS_DETECTION_POSTPROCESSING == 47,
+ "ANEURALNETWORKS_DETECTION_POSTPROCESSING has changed");
+static_assert(ANEURALNETWORKS_EQUAL == 48, "ANEURALNETWORKS_EQUAL has changed");
+static_assert(ANEURALNETWORKS_EXP == 49, "ANEURALNETWORKS_EXP has changed");
+static_assert(ANEURALNETWORKS_EXPAND_DIMS == 50, "ANEURALNETWORKS_EXPAND_DIMS has changed");
+static_assert(ANEURALNETWORKS_GATHER == 51, "ANEURALNETWORKS_GATHER has changed");
+static_assert(ANEURALNETWORKS_GENERATE_PROPOSALS == 52,
+ "ANEURALNETWORKS_GENERATE_PROPOSALS has changed");
+static_assert(ANEURALNETWORKS_GREATER == 53, "ANEURALNETWORKS_GREATER has changed");
+static_assert(ANEURALNETWORKS_GREATER_EQUAL == 54, "ANEURALNETWORKS_GREATER_EQUAL has changed");
+static_assert(ANEURALNETWORKS_GROUPED_CONV_2D == 55, "ANEURALNETWORKS_GROUPED_CONV_2D has changed");
+static_assert(ANEURALNETWORKS_HEATMAP_MAX_KEYPOINT == 56,
+ "ANEURALNETWORKS_HEATMAP_MAX_KEYPOINT has changed");
+static_assert(ANEURALNETWORKS_INSTANCE_NORMALIZATION == 57,
+ "ANEURALNETWORKS_INSTANCE_NORMALIZATION has changed");
+static_assert(ANEURALNETWORKS_LESS == 58, "ANEURALNETWORKS_LESS has changed");
+static_assert(ANEURALNETWORKS_LESS_EQUAL == 59, "ANEURALNETWORKS_LESS_EQUAL has changed");
+static_assert(ANEURALNETWORKS_LOG == 60, "ANEURALNETWORKS_LOG has changed");
+static_assert(ANEURALNETWORKS_LOGICAL_AND == 61, "ANEURALNETWORKS_LOGICAL_AND has changed");
+static_assert(ANEURALNETWORKS_LOGICAL_NOT == 62, "ANEURALNETWORKS_LOGICAL_NOT has changed");
+static_assert(ANEURALNETWORKS_LOGICAL_OR == 63, "ANEURALNETWORKS_LOGICAL_OR has changed");
+static_assert(ANEURALNETWORKS_LOG_SOFTMAX == 64, "ANEURALNETWORKS_LOG_SOFTMAX has changed");
+static_assert(ANEURALNETWORKS_MAXIMUM == 65, "ANEURALNETWORKS_MAXIMUM has changed");
+static_assert(ANEURALNETWORKS_MINIMUM == 66, "ANEURALNETWORKS_MINIMUM has changed");
+static_assert(ANEURALNETWORKS_NEG == 67, "ANEURALNETWORKS_NEG has changed");
+static_assert(ANEURALNETWORKS_NOT_EQUAL == 68, "ANEURALNETWORKS_NOT_EQUAL has changed");
+static_assert(ANEURALNETWORKS_PAD_V2 == 69, "ANEURALNETWORKS_PAD_V2 has changed");
+static_assert(ANEURALNETWORKS_POW == 70, "ANEURALNETWORKS_POW has changed");
+static_assert(ANEURALNETWORKS_PRELU == 71, "ANEURALNETWORKS_PRELU has changed");
+static_assert(ANEURALNETWORKS_QUANTIZE == 72, "ANEURALNETWORKS_QUANTIZE has changed");
+static_assert(ANEURALNETWORKS_QUANTIZED_16BIT_LSTM == 73,
+ "ANEURALNETWORKS_QUANTIZED_16BIT_LSTM has changed");
+static_assert(ANEURALNETWORKS_RANDOM_MULTINOMIAL == 74,
+ "ANEURALNETWORKS_RANDOM_MULTINOMIAL has changed");
+static_assert(ANEURALNETWORKS_REDUCE_ALL == 75, "ANEURALNETWORKS_REDUCE_ALL has changed");
+static_assert(ANEURALNETWORKS_REDUCE_ANY == 76, "ANEURALNETWORKS_REDUCE_ANY has changed");
+static_assert(ANEURALNETWORKS_REDUCE_MAX == 77, "ANEURALNETWORKS_REDUCE_MAX has changed");
+static_assert(ANEURALNETWORKS_REDUCE_MIN == 78, "ANEURALNETWORKS_REDUCE_MIN has changed");
+static_assert(ANEURALNETWORKS_REDUCE_PROD == 79, "ANEURALNETWORKS_REDUCE_PROD has changed");
+static_assert(ANEURALNETWORKS_REDUCE_SUM == 80, "ANEURALNETWORKS_REDUCE_SUM has changed");
+static_assert(ANEURALNETWORKS_ROI_ALIGN == 81, "ANEURALNETWORKS_ROI_ALIGN has changed");
+static_assert(ANEURALNETWORKS_ROI_POOLING == 82, "ANEURALNETWORKS_ROI_POOLING has changed");
+static_assert(ANEURALNETWORKS_RSQRT == 83, "ANEURALNETWORKS_RSQRT has changed");
+static_assert(ANEURALNETWORKS_SELECT == 84, "ANEURALNETWORKS_SELECT has changed");
+static_assert(ANEURALNETWORKS_SIN == 85, "ANEURALNETWORKS_SIN has changed");
+static_assert(ANEURALNETWORKS_SLICE == 86, "ANEURALNETWORKS_SLICE has changed");
+static_assert(ANEURALNETWORKS_SPLIT == 87, "ANEURALNETWORKS_SPLIT has changed");
+static_assert(ANEURALNETWORKS_SQRT == 88, "ANEURALNETWORKS_SQRT has changed");
+static_assert(ANEURALNETWORKS_TILE == 89, "ANEURALNETWORKS_TILE has changed");
+static_assert(ANEURALNETWORKS_TOPK_V2 == 90, "ANEURALNETWORKS_TOPK_V2 has changed");
+static_assert(ANEURALNETWORKS_TRANSPOSE_CONV_2D == 91,
+ "ANEURALNETWORKS_TRANSPOSE_CONV_2D has changed");
+static_assert(ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM == 92,
+ "ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM has changed");
+static_assert(ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_RNN == 93,
+ "ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_RNN has changed");
+static_assert(ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR == 94,
+ "ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR has changed");
+static_assert(ANEURALNETWORKS_QUANTIZED_LSTM == 95, "ANEURALNETWORKS_QUANTIZED_LSTM has changed");
+static_assert(ANEURALNETWORKS_IF == 96, "ANEURALNETWORKS_IF has changed");
+static_assert(ANEURALNETWORKS_WHILE == 97, "ANEURALNETWORKS_WHILE has changed");
+static_assert(ANEURALNETWORKS_ELU == 98, "ANEURALNETWORKS_ELU has changed");
+static_assert(ANEURALNETWORKS_HARD_SWISH == 99, "ANEURALNETWORKS_HARD_SWISH has changed");
+static_assert(ANEURALNETWORKS_FILL == 100, "ANEURALNETWORKS_FILL has changed");
+static_assert(ANEURALNETWORKS_RANK == 101, "ANEURALNETWORKS_RANK has changed");
+static_assert(ANEURALNETWORKS_BATCH_MATMUL == 102, "ANEURALNETWORKS_BATCH_MATMUL has changed");
+static_assert(ANEURALNETWORKS_PACK == 103, "ANEURALNETWORKS_PACK has changed");
+static_assert(ANEURALNETWORKS_MIRROR_PAD == 104, "ANEURALNETWORKS_MIRROR_PAD has changed");
+static_assert(ANEURALNETWORKS_REVERSE == 105, "ANEURALNETWORKS_REVERSE has changed");
+static_assert(ANEURALNETWORKS_OEM_OPERATION == 10000, "ANEURALNETWORKS_OEM_OPERATION has changed");
+
+static_assert(ANEURALNETWORKS_FUSED_NONE == 0, "ANEURALNETWORKS_FUSED_NONE has changed");
+static_assert(ANEURALNETWORKS_FUSED_RELU == 1, "ANEURALNETWORKS_FUSED_RELU has changed");
+static_assert(ANEURALNETWORKS_FUSED_RELU1 == 2, "ANEURALNETWORKS_FUSED_RELU1 has changed");
+static_assert(ANEURALNETWORKS_FUSED_RELU6 == 3, "ANEURALNETWORKS_FUSED_RELU6 has changed");
+
+static_assert(ANEURALNETWORKS_PREFER_LOW_POWER == 0,
+ "ANEURALNETWORKS_PREFER_LOW_POWER has changed");
+static_assert(ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER == 1,
+ "ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER has changed");
+static_assert(ANEURALNETWORKS_PREFER_SUSTAINED_SPEED == 2,
+ "ANEURALNETWORKS_PREFER_SUSTAINED_SPEED has changed");
+
+static_assert(ANEURALNETWORKS_NO_ERROR == 0, "ANEURALNETWORKS_NO_ERROR has changed");
+static_assert(ANEURALNETWORKS_OUT_OF_MEMORY == 1, "ANEURALNETWORKS_OUT_OF_MEMORY has changed");
+static_assert(ANEURALNETWORKS_INCOMPLETE == 2, "ANEURALNETWORKS_INCOMPLETE has changed");
+static_assert(ANEURALNETWORKS_UNEXPECTED_NULL == 3, "ANEURALNETWORKS_UNEXPECTED_NULL has changed");
+static_assert(ANEURALNETWORKS_BAD_DATA == 4, "ANEURALNETWORKS_BAD_DATA has changed");
+static_assert(ANEURALNETWORKS_OP_FAILED == 5, "ANEURALNETWORKS_OP_FAILED has changed");
+static_assert(ANEURALNETWORKS_BAD_STATE == 6, "ANEURALNETWORKS_BAD_STATE has changed");
+static_assert(ANEURALNETWORKS_UNMAPPABLE == 7, "ANEURALNETWORKS_UNMAPPABLE has changed");
+static_assert(ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE == 8,
+ "ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE has changed");
+static_assert(ANEURALNETWORKS_UNAVAILABLE_DEVICE == 9,
+ "ANEURALNETWORKS_UNAVAILABLE_DEVICE has changed");
+static_assert(ANEURALNETWORKS_MISSED_DEADLINE_TRANSIENT == 10,
+ "ANEURALNETWORKS_MISSED_DEADLINE_TRANSIENT has changed");
+static_assert(ANEURALNETWORKS_MISSED_DEADLINE_PERSISTENT == 11,
+ "ANEURALNETWORKS_MISSED_DEADLINE_PERSISTENT has changed");
+static_assert(ANEURALNETWORKS_RESOURCE_EXHAUSTED_TRANSIENT == 12,
+ "ANEURALNETWORKS_RESOURCE_EXHAUSTED_TRANSIENT has changed");
+static_assert(ANEURALNETWORKS_RESOURCE_EXHAUSTED_PERSISTENT == 13,
+ "ANEURALNETWORKS_RESOURCE_EXHAUSTED_PERSISTENT has changed");
+static_assert(ANEURALNETWORKS_DEAD_OBJECT == 14, "ANEURALNETWORKS_DEAD_OBJECT has changed");
+
+static_assert(ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES == 128,
+ "ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES has changed");
+
+static_assert(ANEURALNETWORKS_DEVICE_UNKNOWN == 0, "ANEURALNETWORKS_DEVICE_UNKNOWN has changed");
+static_assert(ANEURALNETWORKS_DEVICE_OTHER == 1, "ANEURALNETWORKS_DEVICE_OTHER has changed");
+static_assert(ANEURALNETWORKS_DEVICE_CPU == 2, "ANEURALNETWORKS_DEVICE_CPU has changed");
+static_assert(ANEURALNETWORKS_DEVICE_GPU == 3, "ANEURALNETWORKS_DEVICE_GPU has changed");
+static_assert(ANEURALNETWORKS_DEVICE_ACCELERATOR == 4,
+ "ANEURALNETWORKS_DEVICE_ACCELERATOR has changed");
+
+static_assert(ANEURALNETWORKS_DURATION_ON_HARDWARE == 0,
+ "ANEURALNETWORKS_DURATION_ON_HARDWARE has changed");
+static_assert(ANEURALNETWORKS_DURATION_IN_DRIVER == 1,
+ "ANEURALNETWORKS_DURATION_IN_DRIVER has changed");
+static_assert(ANEURALNETWORKS_FENCED_DURATION_ON_HARDWARE == 2,
+ "ANEURALNETWORKS_FENCED_DURATION_ON_HARDWARE has changed");
+static_assert(ANEURALNETWORKS_FENCED_DURATION_IN_DRIVER == 3,
+ "ANEURALNETWORKS_FENCED_DURATION_IN_DRIVER has changed");
+
+// Make sure that the constants are compatible with the values defined in
+// hardware/interfaces/neuralnetworks/1.0/types.hal.
+static_assert(static_cast<int32_t>(OperandType::OEM) == ANEURALNETWORKS_OEM_SCALAR,
+ "OEM != ANEURALNETWORKS_OEM");
+static_assert(static_cast<int32_t>(OperandType::FLOAT32) == ANEURALNETWORKS_FLOAT32,
+ "FLOAT32 != ANEURALNETWORKS_FLOAT32");
+static_assert(static_cast<int32_t>(OperandType::INT32) == ANEURALNETWORKS_INT32,
+ "INT32 != ANEURALNETWORKS_INT32");
+static_assert(static_cast<int32_t>(OperandType::UINT32) == ANEURALNETWORKS_UINT32,
+ "UINT32 != ANEURALNETWORKS_UINT32");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_OEM_BYTE) == ANEURALNETWORKS_TENSOR_OEM_BYTE,
+ "TENSOR_OEM_BYTE != ANEURALNETWORKS_TENSOR_OEM_BYTE");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_FLOAT32) == ANEURALNETWORKS_TENSOR_FLOAT32,
+ "TENSOR_FLOAT32 != ANEURALNETWORKS_TENSOR_FLOAT32");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_QUANT8_ASYMM) ==
+ ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
+ "TENSOR_QUANT8_ASYMM != ANEURALNETWORKS_TENSOR_QUANT8_ASYMM");
+
+static_assert(static_cast<int32_t>(OperationType::ADD) == ANEURALNETWORKS_ADD,
+ "OperationType::ADD != ANEURALNETWORKS_ADD");
+static_assert(static_cast<int32_t>(OperationType::AVERAGE_POOL_2D) ==
+ ANEURALNETWORKS_AVERAGE_POOL_2D,
+ "OperationType::AVERAGE_POOL_2D != ANEURALNETWORKS_AVERAGE_POOL_2D");
+static_assert(static_cast<int32_t>(OperationType::CONV_2D) == ANEURALNETWORKS_CONV_2D,
+ "OperationType::CONV_2D != ANEURALNETWORKS_CONV_2D");
+static_assert(static_cast<int32_t>(OperationType::DEPTHWISE_CONV_2D) ==
+ ANEURALNETWORKS_DEPTHWISE_CONV_2D,
+ "OperationType::DEPTHWISE_CONV_2D != ANEURALNETWORKS_DEPTHWISE_CONV_2D");
+static_assert(static_cast<int32_t>(OperationType::DEPTH_TO_SPACE) == ANEURALNETWORKS_DEPTH_TO_SPACE,
+ "OperationType::DEPTH_TO_SPACE != ANEURALNETWORKS_DEPTH_TO_SPACE");
+static_assert(static_cast<int32_t>(OperationType::DEQUANTIZE) == ANEURALNETWORKS_DEQUANTIZE,
+ "OperationType::DEQUANTIZE != ANEURALNETWORKS_DEQUANTIZE");
+static_assert(static_cast<int32_t>(OperationType::EMBEDDING_LOOKUP) ==
+ ANEURALNETWORKS_EMBEDDING_LOOKUP,
+ "OperationType::EMBEDDING_LOOKUP != ANEURALNETWORKS_EMBEDDING_LOOKUP");
+static_assert(static_cast<int32_t>(OperationType::FLOOR) == ANEURALNETWORKS_FLOOR,
+ "OperationType::FLOOR != ANEURALNETWORKS_FLOOR");
+static_assert(static_cast<int32_t>(OperationType::FULLY_CONNECTED) ==
+ ANEURALNETWORKS_FULLY_CONNECTED,
+ "OperationType::FULLY_CONNECTED != ANEURALNETWORKS_FULLY_CONNECTED");
+static_assert(static_cast<int32_t>(OperationType::HASHTABLE_LOOKUP) ==
+ ANEURALNETWORKS_HASHTABLE_LOOKUP,
+ "OperationType::HASHTABLE_LOOKUP != ANEURALNETWORKS_HASHTABLE_LOOKUP");
+static_assert(static_cast<int32_t>(OperationType::L2_NORMALIZATION) ==
+ ANEURALNETWORKS_L2_NORMALIZATION,
+ "OperationType::L2_NORMALIZATION != ANEURALNETWORKS_L2_NORMALIZATION");
+static_assert(static_cast<int32_t>(OperationType::L2_POOL_2D) == ANEURALNETWORKS_L2_POOL_2D,
+ "OperationType::L2_POOL_2D != ANEURALNETWORKS_L2_POOL_2D");
+static_assert(static_cast<int32_t>(OperationType::LOCAL_RESPONSE_NORMALIZATION) ==
+ ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION,
+ "OperationType::LOCAL_RESPONSE_NORMALIZATION != "
+ "ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION");
+static_assert(static_cast<int32_t>(OperationType::LOGISTIC) == ANEURALNETWORKS_LOGISTIC,
+ "OperationType::LOGISTIC != ANEURALNETWORKS_LOGISTIC");
+static_assert(static_cast<int32_t>(OperationType::LSH_PROJECTION) == ANEURALNETWORKS_LSH_PROJECTION,
+ "OperationType::LSH_PROJECTION != ANEURALNETWORKS_LSH_PROJECTION");
+static_assert(static_cast<int32_t>(OperationType::LSTM) == ANEURALNETWORKS_LSTM,
+ "OperationType::LSTM != ANEURALNETWORKS_LSTM");
+static_assert(static_cast<int32_t>(OperationType::MAX_POOL_2D) == ANEURALNETWORKS_MAX_POOL_2D,
+ "OperationType::MAX_POOL_2D != ANEURALNETWORKS_MAX_POOL_2D");
+static_assert(static_cast<int32_t>(OperationType::MUL) == ANEURALNETWORKS_MUL,
+ "OperationType::MUL != ANEURALNETWORKS_MUL");
+static_assert(static_cast<int32_t>(OperationType::RELU) == ANEURALNETWORKS_RELU,
+ "OperationType::RELU != ANEURALNETWORKS_RELU");
+static_assert(static_cast<int32_t>(OperationType::RELU1) == ANEURALNETWORKS_RELU1,
+ "OperationType::RELU1 != ANEURALNETWORKS_RELU1");
+static_assert(static_cast<int32_t>(OperationType::RELU6) == ANEURALNETWORKS_RELU6,
+ "OperationType::RELU6 != ANEURALNETWORKS_RELU6");
+static_assert(static_cast<int32_t>(OperationType::RESHAPE) == ANEURALNETWORKS_RESHAPE,
+ "OperationType::RESHAPE != ANEURALNETWORKS_RESHAPE");
+static_assert(static_cast<int32_t>(OperationType::RESIZE_BILINEAR) ==
+ ANEURALNETWORKS_RESIZE_BILINEAR,
+ "OperationType::RESIZE_BILINEAR != ANEURALNETWORKS_RESIZE_BILINEAR");
+static_assert(static_cast<int32_t>(OperationType::RNN) == ANEURALNETWORKS_RNN,
+ "OperationType::RNN != ANEURALNETWORKS_RNN");
+static_assert(static_cast<int32_t>(OperationType::SOFTMAX) == ANEURALNETWORKS_SOFTMAX,
+ "OperationType::SOFTMAX != ANEURALNETWORKS_SOFTMAX");
+static_assert(static_cast<int32_t>(OperationType::SPACE_TO_DEPTH) == ANEURALNETWORKS_SPACE_TO_DEPTH,
+ "OperationType::SPACE_TO_DEPTH != ANEURALNETWORKS_SPACE_TO_DEPTH");
+static_assert(static_cast<int32_t>(OperationType::SVDF) == ANEURALNETWORKS_SVDF,
+ "OperationType::SVDF != ANEURALNETWORKS_SVDF");
+static_assert(static_cast<int32_t>(OperationType::TANH) == ANEURALNETWORKS_TANH,
+ "OperationType::TANH != ANEURALNETWORKS_TANH");
+
+static_assert(static_cast<int32_t>(FusedActivationFunc::NONE) == ANEURALNETWORKS_FUSED_NONE,
+ "FusedActivationFunc::NONE != ANEURALNETWORKS_FUSED_NONE");
+static_assert(static_cast<int32_t>(FusedActivationFunc::RELU) == ANEURALNETWORKS_FUSED_RELU,
+ "FusedActivationFunc::RELU != ANEURALNETWORKS_FUSED_RELU");
+static_assert(static_cast<int32_t>(FusedActivationFunc::RELU1) == ANEURALNETWORKS_FUSED_RELU1,
+ "FusedActivationFunc::RELU1 != ANEURALNETWORKS_FUSED_RELU1");
+static_assert(static_cast<int32_t>(FusedActivationFunc::RELU6) == ANEURALNETWORKS_FUSED_RELU6,
+ "FusedActivationFunc::RELU6 != ANEURALNETWORKS_FUSED_RELU6");
+
+// Make sure that the constants are compatible with the values defined in
+// hardware/interfaces/neuralnetworks/1.1/types.hal.
+static_assert(static_cast<int32_t>(OperationType::BATCH_TO_SPACE_ND) ==
+ ANEURALNETWORKS_BATCH_TO_SPACE_ND,
+ "OperationType::BATCH_TO_SPACE_ND != ANEURALNETWORKS_BATCH_TO_SPACE_ND");
+static_assert(static_cast<int32_t>(OperationType::DIV) == ANEURALNETWORKS_DIV,
+ "OperationType::DIV != ANEURALNETWORKS_DIV");
+static_assert(static_cast<int32_t>(OperationType::MEAN) == ANEURALNETWORKS_MEAN,
+ "OperationType::MEAN != ANEURALNETWORKS_MEAN");
+static_assert(static_cast<int32_t>(OperationType::PAD) == ANEURALNETWORKS_PAD,
+ "OperationType::PAD != ANEURALNETWORKS_PAD");
+static_assert(static_cast<int32_t>(OperationType::SPACE_TO_BATCH_ND) ==
+ ANEURALNETWORKS_SPACE_TO_BATCH_ND,
+ "OperationType::SPACE_TO_BATCH_ND != ANEURALNETWORKS_SPACE_TO_BATCH_ND");
+static_assert(static_cast<int32_t>(OperationType::SQUEEZE) == ANEURALNETWORKS_SQUEEZE,
+ "OperationType::SQUEEZE != ANEURALNETWORKS_SQUEEZE");
+static_assert(static_cast<int32_t>(OperationType::STRIDED_SLICE) == ANEURALNETWORKS_STRIDED_SLICE,
+ "OperationType::STRIDED_SLICE != ANEURALNETWORKS_STRIDED_SLICE");
+static_assert(static_cast<int32_t>(OperationType::SUB) == ANEURALNETWORKS_SUB,
+ "OperationType::SUB != ANEURALNETWORKS_SUB");
+static_assert(static_cast<int32_t>(OperationType::TRANSPOSE) == ANEURALNETWORKS_TRANSPOSE,
+ "OperationType::TRANSPOSE != ANEURALNETWORKS_TRANSPOSE");
+
+// Make sure that the constants are compatible with the values defined in
+// hardware/interfaces/neuralnetworks/1.2/types.hal.
+static_assert(static_cast<int32_t>(OperandType::BOOL) == ANEURALNETWORKS_BOOL,
+ "BOOL != ANEURALNETWORKS_BOOL");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_QUANT16_SYMM) ==
+ ANEURALNETWORKS_TENSOR_QUANT16_SYMM,
+ "TENSOR_QUANT16_SYMM != ANEURALNETWORKS_TENSOR_QUANT16_SYMM");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_FLOAT16) == ANEURALNETWORKS_TENSOR_FLOAT16,
+ "TENSOR_FLOAT16 != ANEURALNETWORKS_TENSOR_FLOAT16");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_BOOL8) == ANEURALNETWORKS_TENSOR_BOOL8,
+ "TENSOR_BOOL8 != ANEURALNETWORKS_TENSOR_BOOL8");
+static_assert(static_cast<int32_t>(OperandType::FLOAT16) == ANEURALNETWORKS_FLOAT16,
+ "FLOAT16 != ANEURALNETWORKS_FLOAT16");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) ==
+ ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL,
+ "TENSOR_QUANT8_SYMM_PER_CHANNEL != ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_QUANT16_ASYMM) ==
+ ANEURALNETWORKS_TENSOR_QUANT16_ASYMM,
+ "TENSOR_QUANT16_ASYMM != ANEURALNETWORKS_TENSOR_QUANT16_ASYMM");
+static_assert(static_cast<int32_t>(OperandType::TENSOR_QUANT8_SYMM) ==
+ ANEURALNETWORKS_TENSOR_QUANT8_SYMM,
+ "TENSOR_QUANT8_SYMM != ANEURALNETWORKS_TENSOR_QUANT8_SYMM");
+
+static_assert(static_cast<int32_t>(OperationType::ABS) == ANEURALNETWORKS_ABS,
+ "OperationType::ABS != ANEURALNETWORKS_ABS");
+static_assert(static_cast<int32_t>(OperationType::ARGMAX) == ANEURALNETWORKS_ARGMAX,
+ "OperationType::ARGMAX != ANEURALNETWORKS_ARGMAX");
+static_assert(static_cast<int32_t>(OperationType::ARGMIN) == ANEURALNETWORKS_ARGMIN,
+ "OperationType::ARGMIN != ANEURALNETWORKS_ARGMIN");
+static_assert(static_cast<int32_t>(OperationType::AXIS_ALIGNED_BBOX_TRANSFORM) ==
+ ANEURALNETWORKS_AXIS_ALIGNED_BBOX_TRANSFORM,
+ "OperationType::AXIS_ALIGNED_BBOX_TRANSFORM != "
+ "ANEURALNETWORKS_AXIS_ALIGNED_BBOX_TRANSFORM");
+static_assert(static_cast<int32_t>(OperationType::BIDIRECTIONAL_SEQUENCE_LSTM) ==
+ ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_LSTM,
+ "OperationType::BIDIRECTIONAL_SEQUENCE_LSTM != "
+ "ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_LSTM");
+static_assert(
+ static_cast<int32_t>(OperationType::BIDIRECTIONAL_SEQUENCE_RNN) ==
+ ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_RNN,
+ "OperationType::BIDIRECTIONAL_SEQUENCE_RNN != ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_RNN");
+static_assert(static_cast<int32_t>(OperationType::BOX_WITH_NMS_LIMIT) ==
+ ANEURALNETWORKS_BOX_WITH_NMS_LIMIT,
+ "OperationType::BOX_WITH_NMS_LIMIT != ANEURALNETWORKS_BOX_WITH_NMS_LIMIT");
+static_assert(static_cast<int32_t>(OperationType::CAST) == ANEURALNETWORKS_CAST,
+ "OperationType::CAST != ANEURALNETWORKS_CAST");
+static_assert(static_cast<int32_t>(OperationType::CHANNEL_SHUFFLE) ==
+ ANEURALNETWORKS_CHANNEL_SHUFFLE,
+ "OperationType::CHANNEL_SHUFFLE != ANEURALNETWORKS_CHANNEL_SHUFFLE");
+static_assert(
+ static_cast<int32_t>(OperationType::DETECTION_POSTPROCESSING) ==
+ ANEURALNETWORKS_DETECTION_POSTPROCESSING,
+ "OperationType::DETECTION_POSTPROCESSING != ANEURALNETWORKS_DETECTION_POSTPROCESSING");
+static_assert(static_cast<int32_t>(OperationType::EQUAL) == ANEURALNETWORKS_EQUAL,
+ "OperationType::EQUAL != ANEURALNETWORKS_EQUAL");
+static_assert(static_cast<int32_t>(OperationType::EXP) == ANEURALNETWORKS_EXP,
+ "OperationType::EXP != ANEURALNETWORKS_EXP");
+static_assert(static_cast<int32_t>(OperationType::EXPAND_DIMS) == ANEURALNETWORKS_EXPAND_DIMS,
+ "OperationType::EXPAND_DIMS != ANEURALNETWORKS_EXPAND_DIMS");
+static_assert(static_cast<int32_t>(OperationType::GATHER) == ANEURALNETWORKS_GATHER,
+ "OperationType::GATHER != ANEURALNETWORKS_GATHER");
+static_assert(static_cast<int32_t>(OperationType::GENERATE_PROPOSALS) ==
+ ANEURALNETWORKS_GENERATE_PROPOSALS,
+ "OperationType::GENERATE_PROPOSALS != ANEURALNETWORKS_GENERATE_PROPOSALS");
+static_assert(static_cast<int32_t>(OperationType::GREATER) == ANEURALNETWORKS_GREATER,
+ "OperationType::GREATER != ANEURALNETWORKS_GREATER");
+static_assert(static_cast<int32_t>(OperationType::GREATER_EQUAL) == ANEURALNETWORKS_GREATER_EQUAL,
+ "OperationType::GREATER_EQUAL != ANEURALNETWORKS_GREATER_EQUAL");
+static_assert(static_cast<int32_t>(OperationType::GROUPED_CONV_2D) ==
+ ANEURALNETWORKS_GROUPED_CONV_2D,
+ "OperationType::GROUPED_CONV_2D != ANEURALNETWORKS_GROUPED_CONV_2D");
+static_assert(static_cast<int32_t>(OperationType::HEATMAP_MAX_KEYPOINT) ==
+ ANEURALNETWORKS_HEATMAP_MAX_KEYPOINT,
+ "OperationType::HEATMAP_MAX_KEYPOINT != ANEURALNETWORKS_HEATMAP_MAX_KEYPOINT");
+static_assert(static_cast<int32_t>(OperationType::INSTANCE_NORMALIZATION) ==
+ ANEURALNETWORKS_INSTANCE_NORMALIZATION,
+ "OperationType::INSTANCE_NORMALIZATION != ANEURALNETWORKS_INSTANCE_NORMALIZATION");
+static_assert(static_cast<int32_t>(OperationType::LESS) == ANEURALNETWORKS_LESS,
+ "OperationType::LESS != ANEURALNETWORKS_LESS");
+static_assert(static_cast<int32_t>(OperationType::LESS_EQUAL) == ANEURALNETWORKS_LESS_EQUAL,
+ "OperationType::LESS_EQUAL != ANEURALNETWORKS_LESS_EQUAL");
+static_assert(static_cast<int32_t>(OperationType::LOG) == ANEURALNETWORKS_LOG,
+ "OperationType::LOG != ANEURALNETWORKS_LOG");
+static_assert(static_cast<int32_t>(OperationType::LOGICAL_AND) == ANEURALNETWORKS_LOGICAL_AND,
+ "OperationType::LOGICAL_AND != ANEURALNETWORKS_LOGICAL_AND");
+static_assert(static_cast<int32_t>(OperationType::LOGICAL_NOT) == ANEURALNETWORKS_LOGICAL_NOT,
+ "OperationType::LOGICAL_NOT != ANEURALNETWORKS_LOGICAL_NOT");
+static_assert(static_cast<int32_t>(OperationType::LOGICAL_OR) == ANEURALNETWORKS_LOGICAL_OR,
+ "OperationType::LOGICAL_OR != ANEURALNETWORKS_LOGICAL_OR");
+static_assert(static_cast<int32_t>(OperationType::LOG_SOFTMAX) == ANEURALNETWORKS_LOG_SOFTMAX,
+ "OperationType::LOG_SOFTMAX != ANEURALNETWORKS_LOG_SOFTMAX");
+static_assert(static_cast<int32_t>(OperationType::MAXIMUM) == ANEURALNETWORKS_MAXIMUM,
+ "OperationType::MAXIMUM != ANEURALNETWORKS_MAXIMUM");
+static_assert(static_cast<int32_t>(OperationType::MINIMUM) == ANEURALNETWORKS_MINIMUM,
+ "OperationType::MINIMUM != ANEURALNETWORKS_MINIMUM");
+static_assert(static_cast<int32_t>(OperationType::NEG) == ANEURALNETWORKS_NEG,
+ "OperationType::NEG != ANEURALNETWORKS_NEG");
+static_assert(static_cast<int32_t>(OperationType::NOT_EQUAL) == ANEURALNETWORKS_NOT_EQUAL,
+ "OperationType::NOT_EQUAL != ANEURALNETWORKS_NOT_EQUAL");
+static_assert(static_cast<int32_t>(OperationType::PAD_V2) == ANEURALNETWORKS_PAD_V2,
+ "OperationType::PAD_V2 != ANEURALNETWORKS_PAD_V2");
+static_assert(static_cast<int32_t>(OperationType::POW) == ANEURALNETWORKS_POW,
+ "OperationType::POW != ANEURALNETWORKS_POW");
+static_assert(static_cast<int32_t>(OperationType::PRELU) == ANEURALNETWORKS_PRELU,
+ "OperationType::PRELU != ANEURALNETWORKS_PRELU");
+static_assert(static_cast<int32_t>(OperationType::QUANTIZE) == ANEURALNETWORKS_QUANTIZE,
+ "OperationType::QUANTIZE != ANEURALNETWORKS_QUANTIZE");
+static_assert(static_cast<int32_t>(OperationType::QUANTIZED_16BIT_LSTM) ==
+ ANEURALNETWORKS_QUANTIZED_16BIT_LSTM,
+ "OperationType::QUANTIZED_16BIT_LSTM != ANEURALNETWORKS_QUANTIZED_16BIT_LSTM");
+static_assert(static_cast<int32_t>(OperationType::RANDOM_MULTINOMIAL) ==
+ ANEURALNETWORKS_RANDOM_MULTINOMIAL,
+ "OperationType::RANDOM_MULTINOMIAL != ANEURALNETWORKS_RANDOM_MULTINOMIAL");
+static_assert(static_cast<int32_t>(OperationType::REDUCE_ALL) == ANEURALNETWORKS_REDUCE_ALL,
+ "OperationType::REDUCE_ALL != ANEURALNETWORKS_REDUCE_ALL");
+static_assert(static_cast<int32_t>(OperationType::REDUCE_ANY) == ANEURALNETWORKS_REDUCE_ANY,
+ "OperationType::REDUCE_ANY != ANEURALNETWORKS_REDUCE_ANY");
+static_assert(static_cast<int32_t>(OperationType::REDUCE_MAX) == ANEURALNETWORKS_REDUCE_MAX,
+ "OperationType::REDUCE_MAX != ANEURALNETWORKS_REDUCE_MAX");
+static_assert(static_cast<int32_t>(OperationType::REDUCE_MIN) == ANEURALNETWORKS_REDUCE_MIN,
+ "OperationType::REDUCE_MIN != ANEURALNETWORKS_REDUCE_MIN");
+static_assert(static_cast<int32_t>(OperationType::REDUCE_PROD) == ANEURALNETWORKS_REDUCE_PROD,
+ "OperationType::REDUCE_PROD != ANEURALNETWORKS_REDUCE_PROD");
+static_assert(static_cast<int32_t>(OperationType::REDUCE_SUM) == ANEURALNETWORKS_REDUCE_SUM,
+ "OperationType::REDUCE_SUM != ANEURALNETWORKS_REDUCE_SUM");
+static_assert(static_cast<int32_t>(OperationType::ROI_ALIGN) == ANEURALNETWORKS_ROI_ALIGN,
+ "OperationType::ROI_ALIGN != ANEURALNETWORKS_ROI_ALIGN");
+static_assert(static_cast<int32_t>(OperationType::ROI_POOLING) == ANEURALNETWORKS_ROI_POOLING,
+ "OperationType::ROI_POOLING != ANEURALNETWORKS_ROI_POOLING");
+static_assert(static_cast<int32_t>(OperationType::RSQRT) == ANEURALNETWORKS_RSQRT,
+ "OperationType::RSQRT != ANEURALNETWORKS_RSQRT");
+static_assert(static_cast<int32_t>(OperationType::SELECT) == ANEURALNETWORKS_SELECT,
+ "OperationType::SELECT != ANEURALNETWORKS_SELECT");
+static_assert(static_cast<int32_t>(OperationType::SIN) == ANEURALNETWORKS_SIN,
+ "OperationType::SIN != ANEURALNETWORKS_SIN");
+static_assert(static_cast<int32_t>(OperationType::SLICE) == ANEURALNETWORKS_SLICE,
+ "OperationType::SLICE != ANEURALNETWORKS_SLICE");
+static_assert(static_cast<int32_t>(OperationType::SPLIT) == ANEURALNETWORKS_SPLIT,
+ "OperationType::SPLIT != ANEURALNETWORKS_SPLIT");
+static_assert(static_cast<int32_t>(OperationType::SQRT) == ANEURALNETWORKS_SQRT,
+ "OperationType::SQRT != ANEURALNETWORKS_SQRT");
+static_assert(static_cast<int32_t>(OperationType::TILE) == ANEURALNETWORKS_TILE,
+ "OperationType::TILE != ANEURALNETWORKS_TILE");
+static_assert(static_cast<int32_t>(OperationType::TOPK_V2) == ANEURALNETWORKS_TOPK_V2,
+ "OperationType::TOPK_V2 != ANEURALNETWORKS_TOPK_V2");
+static_assert(static_cast<int32_t>(OperationType::TRANSPOSE_CONV_2D) ==
+ ANEURALNETWORKS_TRANSPOSE_CONV_2D,
+ "OperationType::TRANSPOSE_CONV_2D != ANEURALNETWORKS_TRANSPOSE_CONV_2D");
+static_assert(static_cast<int32_t>(OperationType::UNIDIRECTIONAL_SEQUENCE_LSTM) ==
+ ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM,
+ "OperationType::UNIDIRECTIONAL_SEQUENCE_LSTM != "
+ "ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM");
+static_assert(static_cast<int32_t>(OperationType::UNIDIRECTIONAL_SEQUENCE_RNN) ==
+ ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_RNN,
+ "OperationType::UNIDIRECTIONAL_SEQUENCE_RNN != "
+ "ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_RNN");
+static_assert(static_cast<int32_t>(OperationType::RESIZE_NEAREST_NEIGHBOR) ==
+ ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR,
+ "OperationType::RESIZE_NEAREST_NEIGHBOR != ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR");
+static_assert(static_cast<int32_t>(OperationType::QUANTIZED_LSTM) == ANEURALNETWORKS_QUANTIZED_LSTM,
+ "OperationType::QUANTIZED_LSTM != ANEURALNETWORKS_QUANTIZED_LSTM");
+static_assert(static_cast<int32_t>(OperationType::IF) == ANEURALNETWORKS_IF,
+ "OperationType::IF != ANEURALNETWORKS_IF");
+static_assert(static_cast<int32_t>(OperationType::WHILE) == ANEURALNETWORKS_WHILE,
+ "OperationType::WHILE != ANEURALNETWORKS_WHILE");
+static_assert(static_cast<int32_t>(OperationType::ELU) == ANEURALNETWORKS_ELU,
+ "OperationType::ELU != ANEURALNETWORKS_ELU");
+static_assert(static_cast<int32_t>(OperationType::HARD_SWISH) == ANEURALNETWORKS_HARD_SWISH,
+ "OperationType::HARD_SWISH != ANEURALNETWORKS_HARD_SWISH");
+static_assert(static_cast<int32_t>(OperationType::FILL) == ANEURALNETWORKS_FILL,
+ "OperationType::FILL != ANEURALNETWORKS_FILL");
+static_assert(static_cast<int32_t>(OperationType::RANK) == ANEURALNETWORKS_RANK,
+ "OperationType::RANK != ANEURALNETWORKS_RANK");
+static_assert(static_cast<int32_t>(OperationType::BATCH_MATMUL) == ANEURALNETWORKS_BATCH_MATMUL,
+ "OperationType::BATCH_MATMUL != ANEURALNETWORKS_BATCH_MATMUL");
+static_assert(static_cast<int32_t>(OperationType::PACK) == ANEURALNETWORKS_PACK,
+ "OperationType::PACK != ANEURALNETWORKS_PACK");
+static_assert(static_cast<int32_t>(OperationType::MIRROR_PAD) == ANEURALNETWORKS_MIRROR_PAD,
+ "OperationType::MIRROR_PAD != ANEURALNETWORKS_MIRROR_PAD");
+static_assert(static_cast<int32_t>(OperationType::REVERSE) == ANEURALNETWORKS_REVERSE,
+ "OperationType::REVERSE != ANEURALNETWORKS_REVERSE");
+
+static_assert(static_cast<int32_t>(DeviceType::OTHER) == ANEURALNETWORKS_DEVICE_OTHER,
+ "DeviceType::OTHER != ANEURALNETWORKS_DEVICE_OTHER");
+static_assert(static_cast<int32_t>(DeviceType::CPU) == ANEURALNETWORKS_DEVICE_CPU,
+ "DeviceType::CPU != ANEURALNETWORKS_DEVICE_CPU");
+static_assert(static_cast<int32_t>(DeviceType::GPU) == ANEURALNETWORKS_DEVICE_GPU,
+ "DeviceType::GPU != ANEURALNETWORKS_DEVICE_GPU");
+static_assert(static_cast<int32_t>(DeviceType::ACCELERATOR) == ANEURALNETWORKS_DEVICE_ACCELERATOR,
+ "DeviceType::ACCELERATOR != ANEURALNETWORKS_DEVICE_ACCELERATOR");
+
+// Make sure that the constants are compatible with the values defined in
+// hardware/interfaces/neuralnetworks/1.3/types.hal.
+static_assert(android::nn::convertToCanonicalPriority(ANEURALNETWORKS_PRIORITY_LOW) ==
+ Priority::LOW,
+ "ANEURALNETWORKS_PRIORITY_LOW does not map to Priority::LOW");
+static_assert(android::nn::convertToCanonicalPriority(ANEURALNETWORKS_PRIORITY_MEDIUM) ==
+ Priority::MEDIUM,
+ "ANEURALNETWORKS_PRIORITY_MEDIUM does not map to Priority::MEDIUM");
+static_assert(android::nn::convertToCanonicalPriority(ANEURALNETWORKS_PRIORITY_HIGH) ==
+ Priority::HIGH,
+ "ANEURALNETWORKS_PRIORITY_HIGH does not map to Priority::HIGH");
+
+// Asserts for ANeuralNetworksOperandType memory layout
+static_assert(offsetof(ANeuralNetworksOperandType, type) == 0,
+ "ANeuralNetworksOperandType.type offset != 0");
+static_assert(offsetof(ANeuralNetworksOperandType, dimensionCount) == 4,
+ "ANeuralNetworksOperandType.dimensionCount offset != 4");
+static_assert(offsetof(ANeuralNetworksOperandType, dimensions) == 8,
+ "ANeuralNetworksOperandType.dimensions offset != 8");
+static_assert(offsetof(ANeuralNetworksOperandType, scale) == 8 + sizeof(void*),
+ "ANeuralNetworksOperandType.scale offset != 8 + sizeof(void*)");
+static_assert(offsetof(ANeuralNetworksOperandType, zeroPoint) == 12 + sizeof(void*),
+ "ANeuralNetworksOperandType.zeroPoint offset != 12 + sizeof(void*)");
+static_assert(sizeof(ANeuralNetworksOperandType) == 16 + sizeof(void*),
+ "ANeuralNetworksOperandType size changed");
+static_assert(alignof(ANeuralNetworksOperandType) == alignof(void*),
+ "ANeuralNetworksOperandType alignment changed");
+
+// Asserts for ANeuralNetworksSymmPerChannelQuantParams memory layout
+static_assert(offsetof(ANeuralNetworksSymmPerChannelQuantParams, channelDim) == 0,
+ "ANeuralNetworksSymmPerChannelQuantParams.channelDim offset != 4 + sizeof(void*)");
+static_assert(offsetof(ANeuralNetworksSymmPerChannelQuantParams, scaleCount) == 4,
+ "ANeuralNetworksSymmPerChannelQuantParams.scaleCount offset != 0");
+static_assert(offsetof(ANeuralNetworksSymmPerChannelQuantParams, scales) == 8,
+ "ANeuralNetworksSymmPerChannelQuantParams.scales offset != 4");
+static_assert(sizeof(ANeuralNetworksSymmPerChannelQuantParams) == 8 + sizeof(void*),
+ "ANeuralNetworksSymmPerChannelQuantParams size != 8 + sizeof(void*)");
+static_assert(alignof(ANeuralNetworksSymmPerChannelQuantParams) == alignof(void*),
+ "ANeuralNetworksOperandType alignment changed");
+
+// Asserts for compilation caching
+static_assert(ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN == 32,
+ "ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN has changed");
+static_assert(ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN == kByteSizeOfCacheToken,
+ "ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN != kByteSizeOfCacheToken");
+
+// Asserts for compilation priority
+static_assert(ANEURALNETWORKS_PRIORITY_LOW == 90, "ANEURALNETWORKS_PRIORITY_LOW has changed");
+static_assert(ANEURALNETWORKS_PRIORITY_MEDIUM == 100,
+ "ANEURALNETWORKS_PRIORITY_MEDIUM has changed");
+static_assert(ANEURALNETWORKS_PRIORITY_HIGH == 110, "ANEURALNETWORKS_PRIORITY_HIGH has changed");
+static_assert(ANEURALNETWORKS_PRIORITY_DEFAULT == ANEURALNETWORKS_PRIORITY_MEDIUM,
+ "ANEURALNETWORKS_PRIORITY_DEFAULT has changed");
+
+// Asserts for feature levels
+static_assert(ANEURALNETWORKS_FEATURE_LEVEL_1 == 27, "ANEURALNETWORKS_FEATURE_LEVEL_1 has changed");
+static_assert(ANEURALNETWORKS_FEATURE_LEVEL_2 == 28, "ANEURALNETWORKS_FEATURE_LEVEL_2 has changed");
+static_assert(ANEURALNETWORKS_FEATURE_LEVEL_3 == 29, "ANEURALNETWORKS_FEATURE_LEVEL_3 has changed");
+static_assert(ANEURALNETWORKS_FEATURE_LEVEL_4 == 30, "ANEURALNETWORKS_FEATURE_LEVEL_4 has changed");
+static_assert(ANEURALNETWORKS_FEATURE_LEVEL_5 == 31, "ANEURALNETWORKS_FEATURE_LEVEL_5 has changed");
+static_assert(ANEURALNETWORKS_FEATURE_LEVEL_6 == 1000006,
+ "ANEURALNETWORKS_FEATURE_LEVEL_6 has changed");
+static_assert(ANEURALNETWORKS_FEATURE_LEVEL_7 == 1000007,
+ "ANEURALNETWORKS_FEATURE_LEVEL_7 has changed");
+static_assert(ANEURALNETWORKS_FEATURE_LEVEL_8 == 1000008,
+ "ANEURALNETWORKS_FEATURE_LEVEL_8 has changed");
+
+int ANeuralNetworks_getDeviceCount(uint32_t* numDevices) {
+ if (numDevices == nullptr) {
+ LOG(ERROR) << "ANeuralNetworks_getDeviceCount passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ *numDevices = DeviceManager::get()->getDrivers().size();
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworks_getDevice(uint32_t devIndex, ANeuralNetworksDevice** device) {
+ if (device == nullptr) {
+ LOG(ERROR) << "ANeuralNetworks_getDevice passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const std::vector<std::shared_ptr<Device>>& devices = DeviceManager::get()->getDrivers();
+ if (devIndex >= devices.size()) {
+ LOG(ERROR) << "ANeuralNetworks_getDevice passed an invalid device index";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ *device = reinterpret_cast<ANeuralNetworksDevice*>(devices.at(devIndex).get());
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksDevice_getName(const ANeuralNetworksDevice* device, const char** name) {
+ if (device == nullptr || name == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksDevice_getName passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const Device* d = reinterpret_cast<const Device*>(device);
+ *name = d->getName().c_str();
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksDevice_getVersion(const ANeuralNetworksDevice* device, const char** version) {
+ if (device == nullptr || version == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksDevice_getVersion passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const Device* d = reinterpret_cast<const Device*>(device);
+ *version = d->getVersionString().c_str();
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksDevice_getType(const ANeuralNetworksDevice* device, int32_t* type) {
+ if (device == nullptr || type == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksDevice_getType passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const Device* d = reinterpret_cast<const Device*>(device);
+ int32_t dType = d->getType();
+ if (dType < 0) {
+ return ANEURALNETWORKS_OP_FAILED;
+ }
+ *type = d->getType();
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+#ifdef NN_DEBUGGABLE
+static int64_t sRuntimeFeatureLevel = 0;
+void forTest_setRuntimeFeatureLevel(int64_t level) {
+ sRuntimeFeatureLevel = level;
+}
+#endif
+
+// Since ANeuralNetworks_getRuntimeFeatureLevel is new in 31 while libneuralnetwork targets
+// "min_sdk_version: 30", calling it should be properly guarded (e.g. __builtin_available).
+// But calling it within the same compilation unit is perfectly fine. Guarding it doesn't
+// make any sense and is simply wrong. (It's available on a system where __builtin_available(30)
+// evaluates to false.)
+// To make the compiler happy we introduce getRuntimeFeatureLevelImpl() and call it within the
+// library.
+static inline int64_t getRuntimeFeatureLevelImpl() {
+#ifdef NN_DEBUGGABLE
+ if (sRuntimeFeatureLevel) {
+ return sRuntimeFeatureLevel;
+ }
+#endif
+ return DeviceManager::get()->getRuntimeFeatureLevel();
+}
+
+int ANeuralNetworksDevice_getFeatureLevel(const ANeuralNetworksDevice* device,
+ int64_t* featureLevel) {
+ if (device == nullptr || featureLevel == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksDevice_getFeatureLevel passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ Device* d = reinterpret_cast<Device*>(const_cast<ANeuralNetworksDevice*>(device));
+ int64_t dFeatureLevel = DeviceManager::versionToFeatureLevel(d->getFeatureLevel().level);
+ if (dFeatureLevel < 0) {
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+ *featureLevel = std::min(getRuntimeFeatureLevelImpl(), dFeatureLevel);
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksDevice_wait(const ANeuralNetworksDevice* device) {
+ if (device == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksDevice_wait passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const Device* d = reinterpret_cast<const Device*>(device);
+ return d->wait();
+}
+
+int ANeuralNetworksModel_getSupportedOperationsForDevices(
+ const ANeuralNetworksModel* model, const ANeuralNetworksDevice* const* devices,
+ uint32_t numDevices, bool* supportedOps) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksModel_getSupportedOperationsForDevices");
+ if (model == nullptr || devices == nullptr || supportedOps == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksModel_getSupportedOperationsForDevices passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ if (numDevices == 0) {
+ LOG(ERROR) << "ANeuralNetworksModel_getSupportedOperationsForDevices passed an empty "
+ "device list";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ const FlatbufferModelBuilder* m = reinterpret_cast<const FlatbufferModelBuilder*>(model);
+ if (!m->isFinished() || !m->isValid()) {
+ LOG(ERROR) << "ANeuralNetworksModel_getSupportedOperationsForDevices passed an unfinished "
+ "or invalid Model";
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ const Model canonicalModel = m->makeModel();
+ const std::vector<uint32_t>& opMap = m->getSortedOperationMapping();
+ // init the output array to false for all the operations.
+ std::fill(supportedOps, supportedOps + opMap.size(), false);
+ for (uint32_t i = 0; i < numDevices; i++) {
+ if (devices[i] == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksModel_getSupportedOperationsForDevices passed a nullptr "
+ "as a device";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ for (uint32_t j = i + 1; j < numDevices; j++) {
+ if (devices[i] == devices[j]) {
+ LOG(ERROR) << "ANeuralNetworksModel_getSupportedOperationsForDevices passed "
+ "duplicate devices";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ Device* d = reinterpret_cast<Device*>(const_cast<ANeuralNetworksDevice*>(devices[i]));
+ const MetaModel metaModel(canonicalModel, DeviceManager::get()->strictSlicing());
+ const std::vector<bool> supportsByDevice = d->getSupportedOperations(metaModel);
+ for (uint32_t j = 0; j < supportsByDevice.size(); j++) {
+ uint32_t originalIdx = opMap[j];
+ supportedOps[originalIdx] |= supportsByDevice[j];
+ }
+ }
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksCompilation_createForDevices(ANeuralNetworksModel* /* model */,
+ const ANeuralNetworksDevice* const* /* devices */,
+ uint32_t /* numDevices */,
+ ANeuralNetworksCompilation** /* compilation */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksCompilation_createForDevices");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_createForDevices unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+struct ExecutionContext {
+ // inputs are always copied before execution while outputs may be set by custom allocation
+ std::vector<void*> outputs;
+ std::vector<size_t> outputSizes;
+ std::vector<bool> isOutputSpecifiedAtIndex;
+ std::vector<const void*> inputs;
+ std::vector<size_t> inputSizes;
+
+ std::unique_ptr<tflite::Interpreter> interpreter;
+
+ ExecutionContext(std::unique_ptr<tflite::Interpreter> interpreter)
+ : outputs(interpreter->outputs().size()),
+ outputSizes(interpreter->outputs().size()),
+ isOutputSpecifiedAtIndex(interpreter->outputs().size(), false),
+ inputs(interpreter->inputs().size()),
+ inputSizes(interpreter->inputs().size()),
+ interpreter(std::move(interpreter)) {}
+};
+
+int ANeuralNetworksExecution_compute(ANeuralNetworksExecution* execution) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_compute");
+ if (!execution) {
+ LOG(ERROR) << "ANeuralNetworksExecution_compute passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ auto context = reinterpret_cast<ExecutionContext*>(execution);
+ if (std::any_of(context->isOutputSpecifiedAtIndex.begin(),
+ context->isOutputSpecifiedAtIndex.end(), [](bool isSet) { return !isSet; })) {
+ LOG(ERROR) << "ANeuralNetworksExecution_compute not all output buffers are specified";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ auto result = context->interpreter->AllocateTensors();
+ if (result != kTfLiteOk) {
+ LOG(ERROR) << "ANeuralNetworksExecution_compute allocate tensors failed";
+ return ANEURALNETWORKS_OP_FAILED;
+ }
+
+ for (uint32_t index = 0; index < context->interpreter->inputs().size(); index++) {
+ const void* buffer = context->inputs[index];
+ if (buffer == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksExecution_compute not all input buffers are specified";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ size_t length = context->inputSizes[index];
+ std::memcpy(context->interpreter->input_tensor(index)->data.raw, buffer, length);
+ }
+
+ if (context->interpreter->Invoke() != kTfLiteOk) {
+ return ANEURALNETWORKS_OP_FAILED;
+ }
+
+ for (uint32_t i = 0; i < context->interpreter->outputs().size(); i++) {
+ if (context->outputs[i] == nullptr) {
+ continue;
+ }
+
+ const size_t bufferSize = context->outputSizes[i];
+ std::memcpy(context->outputs[i], context->interpreter->output_tensor(i)->data.raw,
+ bufferSize);
+ }
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_setMeasureTiming(ANeuralNetworksExecution* /* execution */,
+ bool /* measure */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_setMeasureTiming");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_setMeasureTiming unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_getDuration(const ANeuralNetworksExecution* /* execution */,
+ int32_t /* durationCode */, uint64_t* /* duration */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_getDuration");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_getDuration unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksBurst_create(ANeuralNetworksCompilation* compilation,
+ ANeuralNetworksBurst** burst) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksBurst_create");
+ if (!compilation || !burst) {
+ LOG(ERROR) << "ANeuralNetworksBurst_create passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ CompilationBuilder* c = reinterpret_cast<CompilationBuilder*>(compilation);
+ BurstBuilder* b = nullptr;
+ int result = c->createBurst(&b);
+ *burst = reinterpret_cast<ANeuralNetworksBurst*>(b);
+ return result;
+}
+
+void ANeuralNetworksBurst_free(ANeuralNetworksBurst* burst) {
+ NNTRACE_RT(NNTRACE_PHASE_TERMINATION, "ANeuralNetworksBurst_free");
+ // No validation. Free of nullptr is valid.
+ BurstBuilder* b = reinterpret_cast<BurstBuilder*>(burst);
+ delete b;
+}
+
+int ANeuralNetworksExecution_burstCompute(ANeuralNetworksExecution* /* execution */,
+ ANeuralNetworksBurst* /* burst */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_burstCompute");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_burstCompute unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksMemoryDesc_create(ANeuralNetworksMemoryDesc** desc) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksMemoryDesc_create");
+ if (desc != nullptr) {
+ *desc = nullptr;
+ }
+ if (!desc) {
+ LOG(ERROR) << "ANeuralNetworksMemoryDesc_create passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ auto mb = std::make_unique<MemoryBuilder>();
+ *desc = reinterpret_cast<ANeuralNetworksMemoryDesc*>(mb.release());
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksMemoryDesc_free(ANeuralNetworksMemoryDesc* desc) {
+ NNTRACE_RT(NNTRACE_PHASE_TERMINATION, "ANeuralNetworksMemoryDesc_free");
+ // No validation. Free of nullptr is valid.
+ MemoryBuilder* mb = reinterpret_cast<MemoryBuilder*>(desc);
+ delete mb;
+}
+
+int ANeuralNetworksMemoryDesc_addInputRole(ANeuralNetworksMemoryDesc* desc,
+ const ANeuralNetworksCompilation* compilation,
+ uint32_t index, float frequency) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksMemoryDesc_addInputRole");
+ if (!desc || !compilation) {
+ LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ MemoryBuilder* mb = reinterpret_cast<MemoryBuilder*>(desc);
+ const CompilationBuilder* c = reinterpret_cast<const CompilationBuilder*>(compilation);
+ return mb->addRole(*c, IOType::INPUT, index, frequency);
+}
+
+int ANeuralNetworksMemoryDesc_addOutputRole(ANeuralNetworksMemoryDesc* desc,
+ const ANeuralNetworksCompilation* compilation,
+ uint32_t index, float frequency) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksMemoryDesc_addOutputRole");
+ if (!desc || !compilation) {
+ LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ MemoryBuilder* mb = reinterpret_cast<MemoryBuilder*>(desc);
+ const CompilationBuilder* c = reinterpret_cast<const CompilationBuilder*>(compilation);
+ return mb->addRole(*c, IOType::OUTPUT, index, frequency);
+}
+
+int ANeuralNetworksMemoryDesc_setDimensions(ANeuralNetworksMemoryDesc* desc, uint32_t rank,
+ const uint32_t* dimensions) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksMemoryDesc_setDimensions");
+ if (!desc || (!dimensions && rank > 0)) {
+ LOG(ERROR) << "ANeuralNetworksMemoryDesc_setDimensions passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const std::vector<uint32_t> dims(dimensions, dimensions + rank);
+ MemoryBuilder* mb = reinterpret_cast<MemoryBuilder*>(desc);
+ return mb->setDimensions(dims);
+}
+
+int ANeuralNetworksMemoryDesc_finish(ANeuralNetworksMemoryDesc* desc) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksMemoryDesc_finish");
+ if (!desc) {
+ LOG(ERROR) << "ANeuralNetworksMemoryDesc_finish passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ MemoryBuilder* mb = reinterpret_cast<MemoryBuilder*>(desc);
+ return mb->finish();
+}
+
+int ANeuralNetworksMemory_createFromDesc(const ANeuralNetworksMemoryDesc* desc,
+ ANeuralNetworksMemory** memory) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksMemory_createFromDesc");
+ if (memory != nullptr) {
+ *memory = nullptr;
+ }
+ if (!desc || !memory) {
+ LOG(ERROR) << "ANeuralNetworksMemory_createFromDesc passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const MemoryBuilder* mb = reinterpret_cast<const MemoryBuilder*>(desc);
+ auto [n, m] = mb->allocate();
+ if (n != ANEURALNETWORKS_NO_ERROR) {
+ return n;
+ }
+ *memory = reinterpret_cast<ANeuralNetworksMemory*>(m.release());
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksMemory_copy(const ANeuralNetworksMemory* src, const ANeuralNetworksMemory* dst) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksMemory_copy");
+ if (!src || !dst) {
+ LOG(ERROR) << "ANeuralNetworksMemory_copy passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const RuntimeMemory* s = reinterpret_cast<const RuntimeMemory*>(src);
+ const RuntimeMemory* d = reinterpret_cast<const RuntimeMemory*>(dst);
+ return RuntimeMemory::copy(*s, *d);
+}
+
+int ANeuralNetworksMemory_createFromFd(size_t size, int prot, int fd, size_t offset,
+ ANeuralNetworksMemory** memory) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksMemory_createFromFd");
+ if (memory != nullptr) {
+ *memory = nullptr;
+ }
+ if (!memory) {
+ LOG(ERROR) << "ANeuralNetworksMemory_createFromFd passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ int n = ANEURALNETWORKS_NO_ERROR;
+ std::unique_ptr<MemoryFd> m;
+ std::tie(n, m) = MemoryFd::create(size, prot, fd, offset);
+ if (n != ANEURALNETWORKS_NO_ERROR) {
+ return n;
+ }
+ *memory = reinterpret_cast<ANeuralNetworksMemory*>(m.release());
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksMemory_createFromAHardwareBuffer(const AHardwareBuffer* ahwb,
+ ANeuralNetworksMemory** memory) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksMemory_createFromAHardwareBuffer");
+ if (memory != nullptr) {
+ *memory = nullptr;
+ }
+ if (!ahwb || !memory) {
+ LOG(ERROR) << "ANeuralNetworksMemory_createFromAHardwareBuffer passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ int n = ANEURALNETWORKS_NO_ERROR;
+ std::unique_ptr<MemoryAHWB> m;
+ std::tie(n, m) = MemoryAHWB::create(*ahwb);
+ if (n != ANEURALNETWORKS_NO_ERROR) {
+ return n;
+ }
+ *memory = reinterpret_cast<ANeuralNetworksMemory*>(m.release());
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksMemory_free(ANeuralNetworksMemory* memory) {
+ NNTRACE_RT(NNTRACE_PHASE_TERMINATION, "ANeuralNetworksMemory_free");
+ // No validation. Free of nullptr is valid.
+ RuntimeMemory* m = reinterpret_cast<RuntimeMemory*>(memory);
+ delete m;
+}
+
+int ANeuralNetworksModel_create(ANeuralNetworksModel** model) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_create");
+ initVLogMask();
+ if (!model) {
+ LOG(ERROR) << "ANeuralNetworksModel_create passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = new (std::nothrow) FlatbufferModelBuilder();
+ if (m == nullptr) {
+ *model = nullptr;
+ return ANEURALNETWORKS_OUT_OF_MEMORY;
+ }
+ *model = reinterpret_cast<ANeuralNetworksModel*>(m);
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksModel_free(ANeuralNetworksModel* model) {
+ NNTRACE_RT(NNTRACE_PHASE_TERMINATION, "ANeuralNetworksModel_free");
+ // No validation. Free of nullptr is valid.
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ delete m;
+}
+
+int ANeuralNetworksModel_finish(ANeuralNetworksModel* model) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_finish");
+ if (!model) {
+ LOG(ERROR) << "ANeuralNetworksModel_finish passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->finish();
+}
+
+int ANeuralNetworksModel_addOperand(ANeuralNetworksModel* model,
+ const ANeuralNetworksOperandType* type) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_addOperand");
+ if (!model || !type) {
+ LOG(ERROR) << "ANeuralNetworksModel_addOperand passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->addOperand(*type);
+}
+
+int ANeuralNetworksModel_setOperandValue(ANeuralNetworksModel* model, int32_t index,
+ const void* buffer, size_t length) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_setOperandValue");
+ if (!model || (!buffer && length != 0)) {
+ LOG(ERROR) << "ANeuralNetworksModel_setOperandValue passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->setOperandValue(index, buffer, length);
+}
+
+int ANeuralNetworksModel_setOperandValueFromMemory(ANeuralNetworksModel* model, int32_t index,
+ const ANeuralNetworksMemory* memory,
+ size_t offset, size_t length) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_setOperandValueFromMemory");
+ if (!model || !memory) {
+ LOG(ERROR) << "ANeuralNetworksModel_setOperandValue passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const RuntimeMemory* mem = reinterpret_cast<const RuntimeMemory*>(memory);
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->setOperandValueFromMemory(index, mem, offset, length);
+}
+
+int ANeuralNetworksModel_setOperandValueFromModel(ANeuralNetworksModel* model, int32_t index,
+ const ANeuralNetworksModel* value) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_setOperandValueFromModel");
+ if (!model || !value) {
+ LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromModel passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const FlatbufferModelBuilder* val = reinterpret_cast<const FlatbufferModelBuilder*>(value);
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->setOperandValueFromModel(index, val);
+}
+
+int ANeuralNetworksModel_addOperation(ANeuralNetworksModel* model,
+ ANeuralNetworksOperationType type, uint32_t inputCount,
+ const uint32_t* inputs, uint32_t outputCount,
+ const uint32_t* outputs) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_addOperation");
+ if (!model || !inputs || !outputs) {
+ LOG(ERROR) << "ANeuralNetworksModel_addOperation passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->addOperation(type, inputCount, inputs, outputCount, outputs);
+}
+
+int ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(
+ ANeuralNetworksModel* model, int32_t index,
+ const ANeuralNetworksSymmPerChannelQuantParams* channelQuant) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION,
+ "ANeuralNetworksModel_setOperandSymmPerChannelQuantParams");
+ if (!model || !channelQuant) {
+ LOG(ERROR) << "ANeuralNetworksModel_setOperandSymmPerChannelQuantParams passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->setOperandSymmPerChannelQuantParams(index, *channelQuant);
+}
+
+int ANeuralNetworksModel_identifyInputsAndOutputs(ANeuralNetworksModel* model, uint32_t inputCount,
+ const uint32_t* inputs, uint32_t outputCount,
+ const uint32_t* outputs) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_identifyInputsAndOutputs");
+ if (!model || !inputs || !outputs) {
+ LOG(ERROR) << ("ANeuralNetworksModel_identifyInputsAndOutputs passed a nullptr");
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->identifyInputsAndOutputs(inputCount, inputs, outputCount, outputs);
+}
+
+int ANeuralNetworksModel_relaxComputationFloat32toFloat16(ANeuralNetworksModel* model, bool allow) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_relaxComputationFloat32toFloat16");
+ if (!model) {
+ LOG(ERROR) << ("ANeuralNetworksModel_relaxComputationFloat32toFloat16 passed a nullptr");
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->relaxComputationFloat32toFloat16(allow);
+}
+
+struct CompilationContext {
+ std::unique_ptr<tflite::FlatBufferModel> flatBufferModel;
+ bool isFinished;
+
+ CompilationContext(std::unique_ptr<tflite::FlatBufferModel> flatBufferModel)
+ : flatBufferModel(std::move(flatBufferModel)), isFinished(false) {}
+};
+
+int ANeuralNetworksCompilation_create(ANeuralNetworksModel* model,
+ ANeuralNetworksCompilation** compilation) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksCompilation_create");
+ if (!model || !compilation) {
+ LOG(ERROR) << "ANeuralNetworksCompilation_create passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+
+ auto tfliteModel = m->createTfliteModel();
+ if (!tfliteModel.ok()) {
+ LOG(ERROR) << "ANeuralNetworksCompilation_create error: " << tfliteModel.error();
+ return ANEURALNETWORKS_OP_FAILED;
+ }
+
+ std::unique_ptr<tflite::FlatBufferModel> flatBufferModel =
+ tflite::FlatBufferModel::BuildFromModel(tfliteModel.value());
+ if (!flatBufferModel) {
+ LOG(ERROR) << "ANeuralNetworksCompilation_create error: tflite::BuildFromModel error";
+ return ANEURALNETWORKS_OP_FAILED;
+ }
+
+ std::unique_ptr<CompilationContext> context =
+ std::make_unique<CompilationContext>(std::move(flatBufferModel));
+ *compilation = reinterpret_cast<ANeuralNetworksCompilation*>(context.release());
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksCompilation_free(ANeuralNetworksCompilation* compilation) {
+ NNTRACE_RT(NNTRACE_PHASE_TERMINATION, "ANeuralNetworksCompilation_free");
+ // No validation. Free of nullptr is valid.
+ auto c = reinterpret_cast<CompilationContext*>(compilation);
+ delete c;
+}
+
+int ANeuralNetworksCompilation_setPreference(ANeuralNetworksCompilation* /* compilation */,
+ int32_t /* preference */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksCompilation_setPreference");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_setPreference unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksCompilation_setCaching(ANeuralNetworksCompilation* /* compilation */,
+ const char* /* cacheDir */, const uint8_t* /* token */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksCompilation_setCaching");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_setCaching unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksCompilation_finish(ANeuralNetworksCompilation* compilation) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksCompilation_finish");
+ if (!compilation) {
+ LOG(ERROR) << "ANeuralNetworksCompilation_finish passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ auto context = reinterpret_cast<CompilationContext*>(compilation);
+ if (context->isFinished) {
+ LOG(ERROR) << "ANeuralNetworksCompilation_finish has already been called";
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+ context->isFinished = true;
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksCompilation_setPriority(ANeuralNetworksCompilation* /* compilation */,
+ int /* priority */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksCompilation_setPriority");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_setPriority unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksCompilation_setTimeout(ANeuralNetworksCompilation* /* compilation */,
+ uint64_t /* duration */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksCompilation_setTimeout");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_setTimeout unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_create(ANeuralNetworksCompilation* compilation,
+ ANeuralNetworksExecution** execution) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_create");
+ if (!compilation || !execution) {
+ LOG(ERROR) << "ANeuralNetworksExecution_create passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ auto c = reinterpret_cast<CompilationContext*>(compilation);
+
+ tflite::ops::builtin::BuiltinOpResolver resolver;
+ std::unique_ptr<tflite::Interpreter> interpreter;
+ auto status = tflite::InterpreterBuilder(*c->flatBufferModel, resolver)(&interpreter);
+ if (status != kTfLiteOk) {
+ LOG(ERROR) << "ANeuralNetworksExecution_create error: interpreter build status " << status
+ << " != " << kTfLiteOk;
+ return ANEURALNETWORKS_OP_FAILED;
+ }
+
+ std::unique_ptr<ExecutionContext> context =
+ std::make_unique<ExecutionContext>(std::move(interpreter));
+ *execution = reinterpret_cast<ANeuralNetworksExecution*>(context.release());
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksExecution_free(ANeuralNetworksExecution* execution) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_free");
+ // Free of nullptr is valid.
+ auto r = reinterpret_cast<ExecutionContext*>(execution);
+ delete r;
+}
+
+int ANeuralNetworksExecution_getOutputOperandRank(ANeuralNetworksExecution* /* execution */,
+ int32_t /* index */, uint32_t* /* rank */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_getOutputOperandRank");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR)
+ << "ANeuralNetworksExecution_getOutputOperandRank unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_getOutputOperandDimensions(ANeuralNetworksExecution* /* execution */,
+ int32_t /* index */,
+ uint32_t* /* dimensions */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_getOutputOperandDimensions");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_getOutputOperandDimensions unimplemented in Neural "
+ "Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_setInput(ANeuralNetworksExecution* execution, int32_t index,
+ const ANeuralNetworksOperandType* type, const void* buffer,
+ size_t length) {
+ NNTRACE_RT(NNTRACE_PHASE_INPUTS_AND_OUTPUTS, "ANeuralNetworksExecution_setInput");
+ // We do not support dynamic shapes
+ if (type != nullptr) {
+ LOG(ERROR) << "ANeuralNetworksExecution_setInput expected a nullptr for "
+ "ANeuralNetworksOperandType* argument";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ if (!execution || (!buffer && length != 0)) {
+ LOG(ERROR) << "ANeuralNetworksExecution_setInput passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ auto context = reinterpret_cast<ExecutionContext*>(execution);
+ if (index < 0 || index >= static_cast<int32_t>(context->interpreter->inputs().size())) {
+ LOG(ERROR) << "ANeuralNetworksExecution_setInput index out of bounds";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (context->interpreter->input_tensor(index)->bytes != length) {
+ LOG(ERROR)
+ << "ANeuralNetworksExecution_setInput input bytes is different from buffer length";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ context->inputs[index] = buffer;
+ context->inputSizes[index] = length;
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_setInputFromMemory(ANeuralNetworksExecution* /* execution */,
+ int32_t /* index */,
+ const ANeuralNetworksOperandType* /* type */,
+ const ANeuralNetworksMemory* /* memory */,
+ size_t /* offset */, size_t /* length */) {
+ NNTRACE_RT(NNTRACE_PHASE_INPUTS_AND_OUTPUTS, "ANeuralNetworksExecution_setInputFromMemory");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_setInputFromMemory unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_setOutput(ANeuralNetworksExecution* execution, int32_t index,
+ const ANeuralNetworksOperandType* type, void* buffer,
+ size_t length) {
+ NNTRACE_RT(NNTRACE_PHASE_INPUTS_AND_OUTPUTS, "ANeuralNetworksExecution_setOutput");
+ // We do not support dynamic shapes
+ if (type != nullptr) {
+ LOG(ERROR) << "ANeuralNetworksExecution_setOutput expected a nullptr for "
+ "ANeuralNetworksOperandType* argument";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution || (!buffer && length != 0)) {
+ LOG(ERROR) << "ANeuralNetworksExecution_setOutput passed a nullptr ";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ auto context = reinterpret_cast<ExecutionContext*>(execution);
+ if (index < 0 || index >= static_cast<int32_t>(context->interpreter->outputs().size())) {
+ LOG(ERROR) << "ANeuralNetworksExecution_setOutput index out of bounds";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ const size_t bufferSize = std::max<size_t>(length, 1);
+ if (bufferSize != context->interpreter->output_tensor(index)->bytes) {
+ LOG(ERROR) << "ANeuralNetworksExecution_setOutput length is not equal to the output tensor "
+ "size";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ const intptr_t dataPtrValue = reinterpret_cast<intptr_t>(buffer);
+ if (dataPtrValue % tflite::kDefaultTensorAlignment != 0) {
+ context->outputs[index] = buffer;
+ context->outputSizes[index] = bufferSize;
+ } else {
+ TfLiteCustomAllocation allocation = {.data = buffer, .bytes = bufferSize};
+ context->interpreter->SetCustomAllocationForTensor(context->interpreter->outputs()[index],
+ allocation,
+ kTfLiteCustomAllocationFlagsNone);
+ }
+
+ context->isOutputSpecifiedAtIndex[index] = true;
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_setOutputFromMemory(ANeuralNetworksExecution* /* execution */,
+ int32_t /* index */,
+ const ANeuralNetworksOperandType* /* type */,
+ const ANeuralNetworksMemory* /* memory */,
+ size_t /* offset */, size_t /* length */) {
+ NNTRACE_RT(NNTRACE_PHASE_INPUTS_AND_OUTPUTS, "ANeuralNetworksExecution_setOutputFromMemory");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR)
+ << "ANeuralNetworksExecution_setOutputFromMemory unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_startCompute(ANeuralNetworksExecution* /* execution */,
+ ANeuralNetworksEvent** /* event */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_startCompute");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_startCompute unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_setTimeout(ANeuralNetworksExecution* /* execution */,
+ uint64_t /* duration */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_setTimeout");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_setTimeout unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksEvent_wait(ANeuralNetworksEvent* event) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksEvent_wait");
+ if (event == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksEvent_wait passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ IEvent* e = reinterpret_cast<IEvent*>(event);
+ return convertErrorStatusToResultCode(e->wait());
+}
+
+void ANeuralNetworksEvent_free(ANeuralNetworksEvent* event) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksEvent_free");
+ // No validation. Free of nullptr is valid.
+ if (event) {
+ IEvent* e = reinterpret_cast<IEvent*>(event);
+ e->wait();
+ delete e;
+ }
+}
+
+int ANeuralNetworksExecution_setLoopTimeout(ANeuralNetworksExecution* /* execution */,
+ uint64_t /* duration */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_setLoopTimeout");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_setLoopTimeout unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+uint64_t ANeuralNetworks_getDefaultLoopTimeout() {
+ return operation_while::kTimeoutNsDefault;
+}
+
+uint64_t ANeuralNetworks_getMaximumLoopTimeout() {
+ return operation_while::kTimeoutNsMaximum;
+}
+
+int ANeuralNetworksDevice_getExtensionSupport(const ANeuralNetworksDevice* device,
+ const char* extensionName,
+ bool* isExtensionSupported) {
+ if (device == nullptr || extensionName == nullptr || isExtensionSupported == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksDevice_getExtensionSupport passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ const Device* d = reinterpret_cast<const Device*>(device);
+ const auto& supportedExtensions = d->getSupportedExtensions();
+ *isExtensionSupported = std::any_of(supportedExtensions.begin(), supportedExtensions.end(),
+ [extensionName](const auto& supportedExtension) {
+ return supportedExtension.name == extensionName;
+ });
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksModel_getExtensionOperandType(ANeuralNetworksModel* model,
+ const char* extensionName,
+ uint16_t operandCodeWithinExtension,
+ int32_t* type) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_getExtensionOperandType");
+ if (!model || !extensionName || !type) {
+ LOG(ERROR) << "ANeuralNetworksModel_getExtensionOperandType passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->getExtensionType(extensionName, operandCodeWithinExtension, type);
+}
+
+int ANeuralNetworksModel_getExtensionOperationType(ANeuralNetworksModel* model,
+ const char* extensionName,
+ uint16_t operationCodeWithinExtension,
+ ANeuralNetworksOperationType* type) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_getExtensionOperationType");
+ if (!model || !extensionName || !type) {
+ LOG(ERROR) << "ANeuralNetworksModel_getExtensionOperationType passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->getExtensionType(extensionName, operationCodeWithinExtension, type);
+}
+
+int ANeuralNetworksModel_setOperandExtensionData(ANeuralNetworksModel* model, int32_t index,
+ const void* data, size_t length) {
+ NNTRACE_RT(NNTRACE_PHASE_PREPARATION, "ANeuralNetworksModel_setOperandExtensionData");
+ if (!model || (!data && length != 0)) {
+ LOG(ERROR) << "ANeuralNetworksModel_setOperandExtensionData passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ FlatbufferModelBuilder* m = reinterpret_cast<FlatbufferModelBuilder*>(model);
+ return m->setOperandExtensionData(index, data, length);
+}
+
+int ANeuralNetworksCompilation_addExtensionAttribute(ANeuralNetworksCompilation* /* compilation */,
+ const char* /* extensionName */,
+ uint16_t /* attributeCodeWithinExtension */,
+ const void* /* data */, size_t /* length */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION, "ANeuralNetworksCompilation_addExtensionAttribute");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_addExtensionAttribute unimplemented in Neural "
+ "Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_addExtensionAttribute(ANeuralNetworksExecution* /* execution */,
+ const char* /* extensionName */,
+ uint16_t /* attributeCodeWithinExtension */,
+ const void* /* data */, size_t /* length */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_addExtensionAttribute");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR)
+ << "ANeuralNetworksExecution_addExtensionAttribute unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksEvent_createFromSyncFenceFd(int syncFenceFd, ANeuralNetworksEvent** event) {
+ if (event == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksEvent_createFromSyncFenceFd passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ if (syncFenceFd <= 0) {
+ LOG(ERROR) << "ANeuralNetworksEvent_createFromSyncFenceFd passed an invalid fd: "
+ << syncFenceFd;
+ *event = nullptr;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ std::unique_ptr<SyncFenceEvent> e =
+ std::make_unique<SyncFenceEvent>(syncFenceFd, nullptr, nullptr);
+ *event = reinterpret_cast<ANeuralNetworksEvent*>(e.release());
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksEvent_getSyncFenceFd(const ANeuralNetworksEvent* event, int* syncFenceFd) {
+ if (syncFenceFd == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksEvent_getSyncFenceFd passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ *syncFenceFd = -1;
+ if (event == nullptr) {
+ LOG(ERROR) << "ANeuralNetworksEvent_getSyncFenceFd passed a nullptr";
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+ const IEvent* e = reinterpret_cast<const IEvent*>(event);
+ // The client owns the dupped fd, and is responsible for closing it.
+ *syncFenceFd = e->getSyncFenceFd(/*shouldDup*/ true);
+ if (*syncFenceFd <= 0) {
+ LOG(ERROR) << "ANeuralNetworksEvent_getSyncFenceFd unable to get valid sync_fence fd";
+ *syncFenceFd = -1;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_startComputeWithDependencies(
+ ANeuralNetworksExecution* /* execution */,
+ const ANeuralNetworksEvent* const* /* dependencies */, uint32_t /* numOfDependencies */,
+ uint64_t /* duration */, ANeuralNetworksEvent** /* event */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_startComputeWithDependencies");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_startComputeWithDependencies unimplemented in Neural "
+ "Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int64_t ANeuralNetworks_getRuntimeFeatureLevel() {
+ return getRuntimeFeatureLevelImpl();
+}
+
+int ANeuralNetworksExecution_enableInputAndOutputPadding(ANeuralNetworksExecution* /* execution */,
+ bool /* enable */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_enableInputAndOutputPadding");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_enableInputAndOutputPadding unimplemented in Neural "
+ "Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput(
+ const ANeuralNetworksCompilation* /* compilation */, uint32_t /* index */,
+ uint32_t* /* alignment */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION,
+ "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput unimplemented in "
+ "Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput(
+ const ANeuralNetworksCompilation* /* compilation */, uint32_t /* index */,
+ uint32_t* /* padding */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION,
+ "ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput unimplemented in "
+ "Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput(
+ const ANeuralNetworksCompilation* /* compilation */, uint32_t /* index */,
+ uint32_t* /* alignment */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION,
+ "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR)
+ << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput unimplemented in "
+ "Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput(
+ const ANeuralNetworksCompilation* /* compilation */, uint32_t /* index */,
+ uint32_t* /* padding */) {
+ NNTRACE_RT(NNTRACE_PHASE_COMPILATION,
+ "ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput unimplemented in "
+ "Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int ANeuralNetworksExecution_setReusable(ANeuralNetworksExecution* /* execution */,
+ bool /* reusable */) {
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ANeuralNetworksExecution_setReusable");
+ // Not supported yet in NNAPI v2
+ LOG(ERROR) << "ANeuralNetworksExecution_setReusable unimplemented in Neural Networks V2";
+ return ANEURALNETWORKS_OP_FAILED;
+}
diff --git a/runtime/operation_converters/AddOperationConverter.cpp b/runtime/operation_converters/AddOperationConverter.cpp
new file mode 100644
index 0000000..4203a73
--- /dev/null
+++ b/runtime/operation_converters/AddOperationConverter.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AddOperationConverter.h"
+
+#include <vector>
+
+#include "OperationConverterResolver.h"
+#include "SubGraphContext.h"
+
+namespace android {
+namespace nn {
+
+Result<void> AddOperationConverter::convert(const Operation& operation,
+ SubGraphContext* context) const {
+ const Model::Subgraph* subgraph = context->getSubgraph();
+
+ // add opcode for ADD if not added yet
+ uint32_t opCodeIdx = context->addOpCode(OperationType::ADD);
+
+ std::vector<int32_t> inputs = NN_TRY(getArithmeticInputs(operation, context));
+ std::vector<int32_t> outputs = NN_TRY(getArithmeticOutputs(operation, context));
+
+ int baseOptionsIdx = 2;
+
+ // activation
+ const Operand& activationOperand =
+ subgraph->operands[operation.inputs[baseOptionsIdx + kActivationOffset]];
+ NN_RET_CHECK(isOperandConstant(activationOperand));
+ FusedActivationFunc activation = static_cast<FusedActivationFunc>(
+ context->getConstantScalar<int32_t>(activationOperand));
+
+ auto optionsFlatbuffer = tflite::CreateAddOptions(
+ context->getBuilder(),
+ NN_TRY(getTfliteActivation(activation)) /* fused_activation_function */);
+ auto operatorFlatbuffer = tflite::CreateOperatorDirect(
+ context->getBuilder() /* builder */, opCodeIdx /* opcode_index */, &inputs /* inputs */,
+ &outputs /* outputs */,
+ tflite::BuiltinOptions::BuiltinOptions_AddOptions /* builtin_options_type */,
+ optionsFlatbuffer.Union() /* builtin_options */);
+ context->addOperatorFlatbuffer(operatorFlatbuffer);
+
+ return {};
+}
+
+NN_REGISTER_OPERATION_CONVERTER(ADD, AddOperationConverter);
+
+} // namespace nn
+} // namespace android
\ No newline at end of file
diff --git a/runtime/operation_converters/AddOperationConverter.h b/runtime/operation_converters/AddOperationConverter.h
new file mode 100644
index 0000000..ab82bfb
--- /dev/null
+++ b/runtime/operation_converters/AddOperationConverter.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_ADD_OPERATION_CONVERTER_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_ADD_OPERATION_CONVERTER_H
+
+#include <vector>
+
+#include "ArithmeticOperationConverter.h"
+
+namespace android {
+namespace nn {
+
+class AddOperationConverter : public ArithmeticOperationConverterBase {
+ public:
+ Result<void> convert(const Operation& operation, SubGraphContext* context) const override;
+};
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_ADD_OPERATION_CONVERTER_H
\ No newline at end of file
diff --git a/runtime/operation_converters/ArithmeticOperationConverter.cpp b/runtime/operation_converters/ArithmeticOperationConverter.cpp
new file mode 100644
index 0000000..e97a302
--- /dev/null
+++ b/runtime/operation_converters/ArithmeticOperationConverter.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ArithmeticOperationConverter.h"
+
+#include <vector>
+
+#include "OperationConverterResolver.h"
+#include "SubGraphContext.h"
+
+namespace android {
+namespace nn {
+
+Result<std::vector<int32_t>> ArithmeticOperationConverterBase::getArithmeticInputs(
+ const Operation& operation, SubGraphContext* context) const {
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.inputs[kInput1TensorIdx]));
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.inputs[kInput2TensorIdx]));
+ std::vector<int32_t> inputs{
+ context->getTensorIdxFromOperandIdx(operation.inputs[kInput1TensorIdx]),
+ context->getTensorIdxFromOperandIdx(operation.inputs[kInput2TensorIdx])};
+ return inputs;
+}
+
+Result<std::vector<int32_t>> ArithmeticOperationConverterBase::getArithmeticOutputs(
+ const Operation& operation, SubGraphContext* context) const {
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.outputs[kOutputTensorIdx]));
+ std::vector<int32_t> outputs{
+ context->getTensorIdxFromOperandIdx(operation.outputs[kOutputTensorIdx])};
+ return outputs;
+}
+
+} // namespace nn
+} // namespace android
\ No newline at end of file
diff --git a/runtime/operation_converters/ArithmeticOperationConverter.h b/runtime/operation_converters/ArithmeticOperationConverter.h
new file mode 100644
index 0000000..d5dbcf6
--- /dev/null
+++ b/runtime/operation_converters/ArithmeticOperationConverter.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_ARITHMETIC_OPERATION_CONVERTER_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_ARITHMETIC_OPERATION_CONVERTER_H
+
+#include <vector>
+
+#include "OperationConverter.h"
+
+namespace android {
+namespace nn {
+
+class ArithmeticOperationConverterBase : public IOperationConverter {
+ protected:
+ Result<std::vector<int32_t>> getArithmeticInputs(const Operation& operation,
+ SubGraphContext* context) const;
+ Result<std::vector<int32_t>> getArithmeticOutputs(const Operation& operation,
+ SubGraphContext* context) const;
+
+ // Offset locations of BuiltinOption parameters in NNAPI Operand inputs
+ static constexpr int kActivationOffset = 0;
+
+ private:
+ // Locations of Operator inputs in a NNAPI Operation
+ static constexpr int kInput1TensorIdx = 0;
+ static constexpr int kInput2TensorIdx = 1;
+
+ // Location of Operator outputs in a NNAPI Operation
+ static constexpr int kOutputTensorIdx = 0;
+};
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_ARITHMETIC_OPERATION_CONVERTER_H
\ No newline at end of file
diff --git a/runtime/operation_converters/Conv2DOperationConverter.cpp b/runtime/operation_converters/Conv2DOperationConverter.cpp
new file mode 100644
index 0000000..c88ff05
--- /dev/null
+++ b/runtime/operation_converters/Conv2DOperationConverter.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Conv2DOperationConverter.h"
+
+#include <vector>
+
+#include "OperationConverterResolver.h"
+#include "SubGraphContext.h"
+
+namespace android {
+namespace nn {
+
+Result<std::vector<int32_t>> Conv2DOperationConverter::getConv2DInputs(
+ const Operation& operation, SubGraphContext* context) const {
+ NN_RET_CHECK(isOperandConstant(
+ context->getSubgraph()->operands[operation.inputs[kFilterTensorIdx]]));
+
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.inputs[kInputTensorIdx]));
+ // TFLite does not support asymmetric tensors for convolution filters
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.inputs[kFilterTensorIdx],
+ true /* makeSymmetric */));
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.inputs[kBiasTensorIdx]));
+ std::vector<int32_t> inputs{
+ context->getTensorIdxFromOperandIdx(operation.inputs[kInputTensorIdx]),
+ context->getTensorIdxFromOperandIdx(operation.inputs[kFilterTensorIdx]),
+ context->getTensorIdxFromOperandIdx(operation.inputs[kBiasTensorIdx])};
+ return inputs;
+}
+
+Result<std::vector<int32_t>> Conv2DOperationConverter::getConv2DOutputs(
+ const Operation& operation, SubGraphContext* context) const {
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.outputs[kOutputTensorIdx]));
+ std::vector<int32_t> outputs{
+ context->getTensorIdxFromOperandIdx(operation.outputs[kOutputTensorIdx])};
+ return outputs;
+}
+
+Result<int> Conv2DOperationConverter::decomposeExplicitPadding(const Operation& operation,
+ SubGraphContext* context) const {
+ const Model::Subgraph* subgraph = context->getSubgraph();
+ const Operand& inputOperand = subgraph->operands[operation.inputs[0]];
+
+ // add opcode for PAD if it does not exist yet
+ uint32_t opCodeIdx = context->addOpCode(OperationType::PAD);
+
+ // pad options
+ auto padOptionsFlatbuffer = tflite::CreatePadOptions(context->getBuilder());
+
+ // check to make sure padding Operands are constants
+ const Operand& frontWidthPaddingOperand = subgraph->operands[operation.inputs[3]];
+ const Operand& backWidthPaddingOperand = subgraph->operands[operation.inputs[4]];
+ const Operand& frontHeightPaddingOperand = subgraph->operands[operation.inputs[5]];
+ const Operand& backHeightPaddingOperand = subgraph->operands[operation.inputs[6]];
+ NN_RET_CHECK(isOperandConstant(frontWidthPaddingOperand));
+ NN_RET_CHECK(isOperandConstant(backWidthPaddingOperand));
+ NN_RET_CHECK(isOperandConstant(frontHeightPaddingOperand));
+ NN_RET_CHECK(isOperandConstant(backHeightPaddingOperand));
+
+ // get padding params
+ int32_t frontHeightPadding = context->getConstantScalar<int32_t>(frontHeightPaddingOperand);
+ int32_t backHeightPadding = context->getConstantScalar<int32_t>(backHeightPaddingOperand);
+ int32_t frontWidthPadding = context->getConstantScalar<int32_t>(frontWidthPaddingOperand);
+ int32_t backWidthPadding = context->getConstantScalar<int32_t>(backWidthPaddingOperand);
+
+ // build padding buffer
+ const Dimensions& dims = inputOperand.dimensions;
+ int numDimensionsInput = static_cast<int>(dims.size());
+ std::vector<int32_t> paddingData(numDimensionsInput * 2, 0);
+ paddingData[2] = frontHeightPadding;
+ paddingData[3] = backHeightPadding;
+ paddingData[4] = frontWidthPadding;
+ paddingData[5] = backWidthPadding;
+ uint32_t paddingBufferIdx = context->addBufferFromData(
+ reinterpret_cast<uint8_t*>(paddingData.data()), paddingData.size() * sizeof(int32_t));
+
+ // create new tensor for padding
+ std::vector<int32_t> padShape{numDimensionsInput, 2};
+ auto padTensor = tflite::CreateTensorDirect(context->getBuilder(), &padShape /* shape */,
+ tflite::TensorType::TensorType_INT32 /* type */,
+ paddingBufferIdx /* buffer */);
+ int padTensorIdx = context->addTensorFlatbuffer(padTensor);
+
+ // add inputs for padding operation
+ std::vector<int32_t> padInputs = {context->getTensorIdxFromOperandIdx(operation.inputs[0]),
+ padTensorIdx};
+
+ // get dimensions of output of pad operation
+ std::vector<int32_t> padToConv2dShape(dims.begin(), dims.end());
+ // keep unknown height and width dimensions unknown
+ padToConv2dShape[1] = padToConv2dShape[1] != 0
+ ? frontHeightPadding + padToConv2dShape[1] + backHeightPadding
+ : -1;
+ padToConv2dShape[2] = padToConv2dShape[2] != 0
+ ? frontWidthPadding + padToConv2dShape[2] + backWidthPadding
+ : -1;
+ replaceZeroDimensions(&padToConv2dShape);
+
+ // build quantization parameters
+ std::vector<float> scaleVector{inputOperand.scale};
+ std::vector<int64_t> zeroPointVector{inputOperand.zeroPoint};
+ // min and max used to convert TFLite models to TF models, so it is unused in this case and can
+ // be set to 0
+ std::vector<float> minVector{0};
+ std::vector<float> maxVector{0};
+ auto quantizationParams = tflite::CreateQuantizationParametersDirect(
+ context->getBuilder(), &minVector /* min */, &maxVector /* max */,
+ &scaleVector /* scale */, &zeroPointVector /* zero_point */,
+ tflite::QuantizationDetails::QuantizationDetails_NONE /* details_type */);
+
+ // create new tensor to be output of pad & input for conv2d
+ auto padToConv2dTensor = tflite::CreateTensorDirect(
+ context->getBuilder(), &padToConv2dShape /* shape */,
+ NN_TRY(getTensorFlatbufferOperandType(inputOperand.type)) /* type */, 0 /* buffer */,
+ 0 /* name */, quantizationParams /* quantization */);
+ int padToConv2dTensorIdx = context->addTensorFlatbuffer(padToConv2dTensor);
+
+ // set output for padding operation and add to operators
+ std::vector<int32_t> padOutputs{padToConv2dTensorIdx};
+
+ OperatorFlatbuffer padOp = tflite::CreateOperatorDirect(
+ context->getBuilder(), opCodeIdx, &padInputs, &padOutputs,
+ tflite::BuiltinOptions::BuiltinOptions_PadOptions, padOptionsFlatbuffer.Union());
+ context->addOperatorFlatbuffer(padOp);
+
+ // Return tensor index of pad output created
+ return padToConv2dTensorIdx;
+}
+
+Result<void> Conv2DOperationConverter::convert(const Operation& operation,
+ SubGraphContext* context) const {
+ const Model::Subgraph* subgraph = context->getSubgraph();
+
+ // add opcode for CONV_2D if not added yet
+ uint32_t opCodeIdx = context->addOpCode(OperationType::CONV_2D);
+
+ // if there are less than 8 inputs or the input at the 7th index is a BOOL, there is implicit
+ // padding
+ bool isImplicitPadding = false;
+ if (operation.inputs.size() < 8 ||
+ subgraph->operands[operation.inputs[7]].type == OperandType::BOOL) {
+ isImplicitPadding = true;
+ }
+
+ std::vector<int32_t> inputs = NN_TRY(getConv2DInputs(operation, context));
+ std::vector<int32_t> outputs = NN_TRY(getConv2DOutputs(operation, context));
+
+ // if explicit padding, we need to decompose the operation to a separate padding op and a conv2d
+ // op
+ if (!isImplicitPadding) {
+ auto padOpIdx = NN_TRY(decomposeExplicitPadding(operation, context));
+ inputs[0] = padOpIdx;
+ }
+
+ int baseOptionsIdx = 4;
+ tflite::Padding padding;
+ if (isImplicitPadding) {
+ const Operand& paddingTypeOperand = subgraph->operands[operation.inputs[3]];
+ NN_RET_CHECK(isOperandConstant(paddingTypeOperand));
+
+ int32_t paddingType = context->getConstantScalar<int32_t>(paddingTypeOperand);
+ padding = getTFLitePadding(paddingType);
+ } else {
+ padding = tflite::Padding::Padding_VALID;
+ baseOptionsIdx = 7;
+ }
+
+ // check if stride and activation Operands are constant
+ const Operand& strideWOperand =
+ subgraph->operands[operation.inputs[baseOptionsIdx + kStrideWOffset]];
+ const Operand& strideHOperand =
+ subgraph->operands[operation.inputs[baseOptionsIdx + kStrideHOffset]];
+ const Operand& activationOperand =
+ subgraph->operands[operation.inputs[baseOptionsIdx + kActivationOffset]];
+ NN_RET_CHECK(isOperandConstant(strideWOperand));
+ NN_RET_CHECK(isOperandConstant(strideHOperand));
+ NN_RET_CHECK(isOperandConstant(activationOperand));
+
+ // get strides and activation
+ int32_t strideW = context->getConstantScalar<int32_t>(strideWOperand);
+ int32_t strideH = context->getConstantScalar<int32_t>(strideHOperand);
+ FusedActivationFunc activation = static_cast<FusedActivationFunc>(
+ context->getConstantScalar<int32_t>(activationOperand));
+
+ // check for nchw
+ int isNchwIdx = baseOptionsIdx + kIsNchwOffset;
+ if (operation.inputs.size() > static_cast<uint32_t>(isNchwIdx)) {
+ const Operand& isNchwOperand = subgraph->operands[operation.inputs[isNchwIdx]];
+ NN_RET_CHECK(isOperandConstant(isNchwOperand));
+
+ bool isNchw = context->getConstantScalar<bool>(isNchwOperand);
+ NN_RET_CHECK(!isNchw) << "TFLite does not support NCHW formatted input tensors";
+ }
+
+ // dilations
+ int dilationWIdx = baseOptionsIdx + kDilationWOffset;
+ int dilationHIdx = baseOptionsIdx + kDilationHOffset;
+ // default dilation factors are 1
+ int32_t dilationW = 1;
+ int32_t dilationH = 1;
+ if (operation.inputs.size() > static_cast<uint32_t>(dilationWIdx)) {
+ const Operand& dilationWOperand = subgraph->operands[operation.inputs[dilationWIdx]];
+ NN_RET_CHECK(isOperandConstant(dilationWOperand));
+
+ dilationW = context->getConstantScalar<int32_t>(dilationWOperand);
+ }
+ if (operation.inputs.size() > static_cast<uint32_t>(dilationHIdx)) {
+ const Operand& dilationHOperand = subgraph->operands[operation.inputs[dilationHIdx]];
+ NN_RET_CHECK(isOperandConstant(dilationHOperand));
+
+ dilationH = context->getConstantScalar<int32_t>(dilationHOperand);
+ }
+
+ flatbuffers::Offset<tflite::Conv2DOptions> optionsFlatbuffer = tflite::CreateConv2DOptions(
+ context->getBuilder(), padding, strideW, strideH,
+ NN_TRY(getTfliteActivation(activation)) /* fused_activation_function */, dilationW,
+ dilationH);
+ auto operatorFlatbuffer = tflite::CreateOperatorDirect(
+ context->getBuilder() /* builder */, opCodeIdx /* opcode_index */, &inputs /* inputs */,
+ &outputs /* outputs */,
+ tflite::BuiltinOptions::BuiltinOptions_Conv2DOptions /* builtin_options_type */,
+ optionsFlatbuffer.Union() /* builtin_options */);
+ context->addOperatorFlatbuffer(operatorFlatbuffer);
+
+ return {};
+}
+
+NN_REGISTER_OPERATION_CONVERTER(CONV_2D, Conv2DOperationConverter);
+
+} // namespace nn
+} // namespace android
\ No newline at end of file
diff --git a/runtime/operation_converters/Conv2DOperationConverter.h b/runtime/operation_converters/Conv2DOperationConverter.h
new file mode 100644
index 0000000..398aaff
--- /dev/null
+++ b/runtime/operation_converters/Conv2DOperationConverter.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_CONV2D_OPERATION_CONVERTER_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_CONV2D_OPERATION_CONVERTER_H
+
+#include <vector>
+
+#include "OperationConverter.h"
+
+namespace android {
+namespace nn {
+
+class Conv2DOperationConverter : public IOperationConverter {
+ public:
+ Result<void> convert(const Operation& operation, SubGraphContext* context) const override;
+
+ protected:
+ Result<std::vector<int32_t>> getConv2DInputs(const Operation& operation,
+ SubGraphContext* context) const;
+ Result<std::vector<int32_t>> getConv2DOutputs(const Operation& operation,
+ SubGraphContext* context) const;
+
+ // Returns the output Tensor index of created Padding Operator if successful
+ Result<int> decomposeExplicitPadding(const Operation& operation,
+ SubGraphContext* context) const;
+
+ private:
+ // Offset locations of BuiltinOption parameters in NNAPI Operand inputs
+ static constexpr int kStrideWOffset = 0;
+ static constexpr int kStrideHOffset = 1;
+ static constexpr int kActivationOffset = 2;
+ static constexpr int kIsNchwOffset = 3;
+ static constexpr int kDilationWOffset = 4;
+ static constexpr int kDilationHOffset = 5;
+
+ // Locations of Operator inputs in a NNAPI Operation
+ static constexpr int kInputTensorIdx = 0;
+ static constexpr int kFilterTensorIdx = 1;
+ static constexpr int kBiasTensorIdx = 2;
+
+ // Location of Operator outputs in a NNAPI Operation
+ static constexpr int kOutputTensorIdx = 0;
+};
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_CONV2D_OPERATION_CONVERTER_H
\ No newline at end of file
diff --git a/runtime/operation_converters/DepthwiseConv2DOperationConverter.cpp b/runtime/operation_converters/DepthwiseConv2DOperationConverter.cpp
new file mode 100644
index 0000000..eb0e3b5
--- /dev/null
+++ b/runtime/operation_converters/DepthwiseConv2DOperationConverter.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DepthwiseConv2DOperationConverter.h"
+
+#include <vector>
+
+#include "OperationConverterResolver.h"
+#include "SubGraphContext.h"
+
+namespace android {
+namespace nn {
+
+Result<void> DepthwiseConv2DOperationConverter::convert(const Operation& operation,
+ SubGraphContext* context) const {
+ const Model::Subgraph* subgraph = context->getSubgraph();
+
+ // add opcode for DEPTHWISE_CONV_2D if not added yet
+ uint32_t opCodeIdx = context->addOpCode(OperationType::DEPTHWISE_CONV_2D);
+
+ // if there are less than 9 inputs or the input at the 8th index is a BOOL, there is implicit
+ // padding
+ const bool isImplicitPadding =
+ (operation.inputs.size() < 9 ||
+ subgraph->operands[operation.inputs[8]].type == OperandType::BOOL);
+
+ std::vector<int32_t> inputs = NN_TRY(getConv2DInputs(operation, context));
+ std::vector<int32_t> outputs = NN_TRY(getConv2DOutputs(operation, context));
+
+ // if explicit padding, we need to decompose the operation to a separate padding op and a conv2d
+ // op
+ if (!isImplicitPadding) {
+ auto padOpIdx = NN_TRY(decomposeExplicitPadding(operation, context));
+ inputs[0] = padOpIdx;
+ }
+
+ int baseOptionsIdx = 4;
+ tflite::Padding padding;
+ if (isImplicitPadding) {
+ const Operand& paddingTypeOperand = subgraph->operands[operation.inputs[3]];
+ NN_RET_CHECK(isOperandConstant(paddingTypeOperand));
+
+ int32_t paddingType = context->getConstantScalar<int32_t>(paddingTypeOperand);
+ padding = getTFLitePadding(paddingType);
+ } else {
+ padding = tflite::Padding::Padding_VALID;
+ baseOptionsIdx = 7;
+ }
+
+ // check if stride, depthwise multiplier, and activation Operands are constant
+ const Operand& strideWOperand =
+ subgraph->operands[operation.inputs[baseOptionsIdx + kStrideWOffset]];
+ const Operand& strideHOperand =
+ subgraph->operands[operation.inputs[baseOptionsIdx + kStrideHOffset]];
+ const Operand& activationOperand =
+ subgraph->operands[operation.inputs[baseOptionsIdx + kActivationOffset]];
+ const Operand& depthwiseMultiplierOperand =
+ subgraph->operands[operation.inputs[baseOptionsIdx + kDepthwiseMultiplier]];
+ NN_RET_CHECK(isOperandConstant(strideWOperand));
+ NN_RET_CHECK(isOperandConstant(strideHOperand));
+ NN_RET_CHECK(isOperandConstant(activationOperand));
+ NN_RET_CHECK(isOperandConstant(depthwiseMultiplierOperand));
+
+ // get strides and activation
+ int32_t strideW = context->getConstantScalar<int32_t>(strideWOperand);
+ int32_t strideH = context->getConstantScalar<int32_t>(strideHOperand);
+ int32_t depthwiseMultiplier = context->getConstantScalar<int32_t>(depthwiseMultiplierOperand);
+ FusedActivationFunc activation = static_cast<FusedActivationFunc>(
+ context->getConstantScalar<int32_t>(activationOperand));
+
+ // check for nchw
+ int isNchwIdx = baseOptionsIdx + kIsNchwOffset;
+ if (operation.inputs.size() > static_cast<uint32_t>(isNchwIdx)) {
+ const Operand& isNchwOperand = subgraph->operands[operation.inputs[isNchwIdx]];
+ NN_RET_CHECK(isOperandConstant(isNchwOperand));
+
+ bool isNchw = context->getConstantScalar<bool>(isNchwOperand);
+ NN_RET_CHECK(!isNchw) << "TFLite does not support NCHW formatted input tensors";
+ }
+
+ // dilations
+ int dilationWIdx = baseOptionsIdx + kDilationWOffset;
+ int dilationHIdx = baseOptionsIdx + kDilationHOffset;
+ // default dilation factors are 1
+ int32_t dilationW = 1;
+ int32_t dilationH = 1;
+ if (operation.inputs.size() > static_cast<uint32_t>(dilationWIdx)) {
+ const Operand& dilationWOperand = subgraph->operands[operation.inputs[dilationWIdx]];
+ NN_RET_CHECK(isOperandConstant(dilationWOperand));
+
+ dilationW = context->getConstantScalar<int32_t>(dilationWOperand);
+ }
+ if (operation.inputs.size() > static_cast<uint32_t>(dilationHIdx)) {
+ const Operand& dilationHOperand = subgraph->operands[operation.inputs[dilationHIdx]];
+ NN_RET_CHECK(isOperandConstant(dilationHOperand));
+
+ dilationH = context->getConstantScalar<int32_t>(dilationHOperand);
+ }
+
+ flatbuffers::Offset<tflite::DepthwiseConv2DOptions> optionsFlatbuffer =
+ tflite::CreateDepthwiseConv2DOptions(
+ context->getBuilder(), padding, strideW, strideH, depthwiseMultiplier,
+ NN_TRY(getTfliteActivation(activation)) /* fused_activation_function */,
+ dilationW, dilationH);
+ auto operatorFlatbuffer = tflite::CreateOperatorDirect(
+ context->getBuilder() /* builder */, opCodeIdx /* opcode_index */, &inputs /* inputs */,
+ &outputs /* outputs */,
+ tflite::BuiltinOptions::
+ BuiltinOptions_DepthwiseConv2DOptions /* builtin_options_type */,
+ optionsFlatbuffer.Union() /* builtin_options */);
+ context->addOperatorFlatbuffer(operatorFlatbuffer);
+
+ return {};
+}
+
+NN_REGISTER_OPERATION_CONVERTER(DEPTHWISE_CONV_2D, DepthwiseConv2DOperationConverter);
+
+} // namespace nn
+} // namespace android
\ No newline at end of file
diff --git a/runtime/operation_converters/DepthwiseConv2DOperationConverter.h b/runtime/operation_converters/DepthwiseConv2DOperationConverter.h
new file mode 100644
index 0000000..37302d7
--- /dev/null
+++ b/runtime/operation_converters/DepthwiseConv2DOperationConverter.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_DEPTHWISE_CONV2D_OPERATION_CONVERTER_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_DEPTHWISE_CONV2D_OPERATION_CONVERTER_H
+
+#include <vector>
+
+#include "Conv2DOperationConverter.h"
+
+namespace android {
+namespace nn {
+
+class DepthwiseConv2DOperationConverter : public Conv2DOperationConverter {
+ public:
+ Result<void> convert(const Operation& operation, SubGraphContext* context) const override;
+
+ private:
+ // Offset locations of BuiltinOption parameters in NNAPI Operand inputs
+ static constexpr int kStrideWOffset = 0;
+ static constexpr int kStrideHOffset = 1;
+ static constexpr int kDepthwiseMultiplier = 2;
+ static constexpr int kActivationOffset = 3;
+ static constexpr int kIsNchwOffset = 4;
+ static constexpr int kDilationWOffset = 5;
+ static constexpr int kDilationHOffset = 6;
+};
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_DEPTHWISE_CONV2D_OPERATION_CONVERTER_H
\ No newline at end of file
diff --git a/runtime/operation_converters/LogisticOperationConverter.cpp b/runtime/operation_converters/LogisticOperationConverter.cpp
new file mode 100644
index 0000000..20528f4
--- /dev/null
+++ b/runtime/operation_converters/LogisticOperationConverter.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogisticOperationConverter.h"
+
+#include <vector>
+
+#include "OperationConverterResolver.h"
+#include "SubGraphContext.h"
+
+namespace android {
+namespace nn {
+
+Result<std::vector<int32_t>> LogisticOperationConverter::getLogisticInputs(
+ const Operation& operation, SubGraphContext* context) const {
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.inputs[kInputTensorIdx]));
+ std::vector<int32_t> inputs{
+ context->getTensorIdxFromOperandIdx(operation.inputs[kInputTensorIdx])};
+ return inputs;
+}
+
+Result<std::vector<int32_t>> LogisticOperationConverter::getLogisticOutputs(
+ const Operation& operation, SubGraphContext* context) const {
+ NN_TRY(context->createTensorFlatbufferFromOperand(operation.outputs[kOutputTensorIdx]));
+ std::vector<int32_t> outputs{
+ context->getTensorIdxFromOperandIdx(operation.outputs[kOutputTensorIdx])};
+ return outputs;
+}
+
+Result<void> LogisticOperationConverter::convert(const Operation& operation,
+ SubGraphContext* context) const {
+ // add opcode for LOGISTIC if not added yet
+ uint32_t opCodeIdx = context->addOpCode(OperationType::LOGISTIC);
+
+ std::vector<int32_t> inputs = NN_TRY(getLogisticInputs(operation, context));
+ std::vector<int32_t> outputs = NN_TRY(getLogisticOutputs(operation, context));
+
+ auto optionsFlatbuffer = tflite::CreateLogSoftmaxOptions(context->getBuilder());
+ auto operatorFlatbuffer = tflite::CreateOperatorDirect(
+ context->getBuilder() /* builder */, opCodeIdx /* opcode_index */, &inputs /* inputs */,
+ &outputs /* outputs */,
+ tflite::BuiltinOptions::BuiltinOptions_LogSoftmaxOptions /* builtin_options_type */,
+ optionsFlatbuffer.Union() /* builtin_options */);
+ context->addOperatorFlatbuffer(operatorFlatbuffer);
+
+ return {};
+}
+
+NN_REGISTER_OPERATION_CONVERTER(LOGISTIC, LogisticOperationConverter);
+
+} // namespace nn
+} // namespace android
\ No newline at end of file
diff --git a/runtime/operation_converters/LogisticOperationConverter.h b/runtime/operation_converters/LogisticOperationConverter.h
new file mode 100644
index 0000000..dc8dccc
--- /dev/null
+++ b/runtime/operation_converters/LogisticOperationConverter.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_LOGISTIC_OPERATION_CONVERTER_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_LOGISTIC_OPERATION_CONVERTER_H
+
+#include <vector>
+
+#include "OperationConverter.h"
+
+namespace android {
+namespace nn {
+
+class LogisticOperationConverter : public IOperationConverter {
+ public:
+ Result<void> convert(const Operation& operation, SubGraphContext* context) const override;
+
+ private:
+ Result<std::vector<int32_t>> getLogisticInputs(const Operation& operation,
+ SubGraphContext* context) const;
+ Result<std::vector<int32_t>> getLogisticOutputs(const Operation& operation,
+ SubGraphContext* context) const;
+
+ // Location of Operator inputs in a NNAPI Operation
+ static constexpr int kInputTensorIdx = 0;
+
+ // Location of Operator outputs in a NNAPI Operation
+ static constexpr int kOutputTensorIdx = 0;
+};
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_LOGISTIC_OPERATION_CONVERTER_H
\ No newline at end of file
diff --git a/runtime/operation_converters/OperationConverter.h b/runtime/operation_converters/OperationConverter.h
new file mode 100644
index 0000000..abe8d4a
--- /dev/null
+++ b/runtime/operation_converters/OperationConverter.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_OPERATION_CONVERTER_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_OPERATION_CONVERTER_H
+
+#include "SubGraphContext.h"
+
+namespace android {
+namespace nn {
+
+class IOperationConverter {
+ public:
+ virtual ~IOperationConverter() = default;
+
+ virtual Result<void> convert(const Operation& operation, SubGraphContext* context) const = 0;
+};
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_OPERATION_CONVERTER_H
\ No newline at end of file
diff --git a/runtime/operation_converters/OperationConverterResolver.cpp b/runtime/operation_converters/OperationConverterResolver.cpp
new file mode 100644
index 0000000..530b959
--- /dev/null
+++ b/runtime/operation_converters/OperationConverterResolver.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 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 "OperationConverterResolver"
+
+#include "OperationConverterResolver.h"
+
+#include "OperationsUtils.h"
+
+namespace android {
+namespace nn {
+
+#define NN_FORWARD_DECLARE_OPERATION_CONVERTER_REGISTRATION_FUNCTION(opType) \
+ const IOperationConverter* registerConverter_##opType();
+
+NN_FOR_EACH_OPERATION(NN_FORWARD_DECLARE_OPERATION_CONVERTER_REGISTRATION_FUNCTION)
+
+#undef NN_FORWARD_DECLARE_OPERATION_CONVERTER_REGISTRATION_FUNCTION
+
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(AVERAGE_POOL_2D);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(CONCATENATION);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(DEPTH_TO_SPACE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(DEQUANTIZE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(EMBEDDING_LOOKUP);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(FLOOR);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(FULLY_CONNECTED);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(HASHTABLE_LOOKUP);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(L2_NORMALIZATION);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(L2_POOL_2D);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LOCAL_RESPONSE_NORMALIZATION);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LSH_PROJECTION);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LSTM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(MAX_POOL_2D);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(MUL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RELU);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RELU1);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RELU6);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RESHAPE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RESIZE_BILINEAR);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RNN);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SOFTMAX);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SPACE_TO_DEPTH);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SVDF);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(TANH);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(BATCH_TO_SPACE_ND);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(DIV);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(MEAN);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(PAD);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SPACE_TO_BATCH_ND);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SQUEEZE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(STRIDED_SLICE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SUB);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(TRANSPOSE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(ABS);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(ARGMAX);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(ARGMIN);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(AXIS_ALIGNED_BBOX_TRANSFORM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(BIDIRECTIONAL_SEQUENCE_LSTM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(BIDIRECTIONAL_SEQUENCE_RNN);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(BOX_WITH_NMS_LIMIT);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(CAST);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(CHANNEL_SHUFFLE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(DENSIFY);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(DETECTION_POSTPROCESSING);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(EQUAL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(EXP);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(EXPAND_DIMS);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(GATHER);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(GENERATE_PROPOSALS);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(GREATER);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(GREATER_EQUAL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(GROUPED_CONV_2D);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(HEATMAP_MAX_KEYPOINT);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(INSTANCE_NORMALIZATION);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LESS);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LESS_EQUAL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LOG);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LOGICAL_AND);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LOGICAL_NOT);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LOGICAL_OR);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(LOG_SOFTMAX);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(MAXIMUM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(MINIMUM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(NEG);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(NOT_EQUAL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(PAD_V2);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(POW);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(PRELU);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(QUANTIZE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(QUANTIZED_16BIT_LSTM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RANDOM_MULTINOMIAL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(REDUCE_ALL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(REDUCE_ANY);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(REDUCE_MAX);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(REDUCE_MIN);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(REDUCE_PROD);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(REDUCE_SUM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(ROI_ALIGN);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(ROI_POOLING);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RSQRT);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SELECT);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SIN);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SLICE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SPLIT);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(SQRT);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(TILE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(TOPK_V2);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(TRANSPOSE_CONV_2D);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(UNIDIRECTIONAL_SEQUENCE_LSTM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(UNIDIRECTIONAL_SEQUENCE_RNN);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RESIZE_NEAREST_NEIGHBOR);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(QUANTIZED_LSTM);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(IF);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(WHILE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(ELU);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(HARD_SWISH);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(FILL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(RANK);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(BATCH_MATMUL);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(PACK);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(MIRROR_PAD);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(REVERSE);
+NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(OEM_OPERATION);
+
+OperationConverterResolver::OperationConverterResolver() {
+#define NN_REGISTER_OPERATION_CONVERTER_TO_RESOLVER(operationType) \
+ registerOperationConverter(registerConverter_##operationType(), OperationType::operationType);
+ NN_FOR_EACH_OPERATION(NN_REGISTER_OPERATION_CONVERTER_TO_RESOLVER)
+#undef NN_REGISTER_OPERATION_CONVERTER_TO_RESOLVER
+}
+
+const IOperationConverter* OperationConverterResolver::findOperationConverter(
+ OperationType operationType) const {
+ int32_t index = static_cast<int32_t>(operationType);
+ if (index >= 0 && index < kNumberOfOperationTypes) {
+ return mConverters[index];
+ }
+ return nullptr;
+}
+
+void OperationConverterResolver::registerOperationConverter(
+ const IOperationConverter* operationConverter, OperationType operationType) {
+ if (operationConverter == nullptr) {
+ return;
+ }
+
+ int32_t index = static_cast<int32_t>(operationType);
+ CHECK(mConverters[index] == nullptr);
+ mConverters[index] = operationConverter;
+}
+
+} // namespace nn
+} // namespace android
\ No newline at end of file
diff --git a/runtime/operation_converters/OperationConverterResolver.h b/runtime/operation_converters/OperationConverterResolver.h
new file mode 100644
index 0000000..4057799
--- /dev/null
+++ b/runtime/operation_converters/OperationConverterResolver.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_OPERATION_CONVERTER_RESOLVER_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_OPERATION_CONVERTER_RESOLVER_H
+
+#include "OperationConverter.h"
+#include "SubGraphContext.h"
+
+namespace android {
+namespace nn {
+
+// OperationConverterResolver is used to register all operation converters that implement
+// IOperationConverter. This retrieves the correct converter to use based on OperationType
+class OperationConverterResolver {
+ public:
+ static const OperationConverterResolver* get() {
+ static OperationConverterResolver instance;
+ return &instance;
+ }
+ const IOperationConverter* findOperationConverter(OperationType operationType) const;
+
+ private:
+ OperationConverterResolver();
+
+ void registerOperationConverter(const IOperationConverter* operationConverter,
+ OperationType operationType);
+
+ const IOperationConverter* mConverters[kNumberOfOperationTypes] = {};
+};
+
+// Use to register operation converter into OperationConverterResolver
+#define NN_REGISTER_OPERATION_CONVERTER(identifier, OperationConverterClass) \
+ const IOperationConverter* registerConverter_##identifier() { \
+ static OperationConverterClass converter; \
+ return &converter; \
+ }
+
+// Use to indicate which operations are not supported
+#define NN_OPERATION_CONVERTER_NOT_IMPLEMENTED(identifier) \
+ const IOperationConverter* registerConverter_##identifier() { \
+ return nullptr; \
+ }
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_OPERATION_CONVERTER_RESOLVER_H
\ No newline at end of file
diff --git a/runtime/operation_converters/SubGraphContext.cpp b/runtime/operation_converters/SubGraphContext.cpp
new file mode 100644
index 0000000..c4ccb50
--- /dev/null
+++ b/runtime/operation_converters/SubGraphContext.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2022 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 "SubGraphContext"
+
+#include "SubGraphContext.h"
+
+#include <limits>
+
+#include "FlatbufferModelBuilderUtils.h"
+
+namespace android {
+namespace nn {
+
+SubGraphContext::SubGraphContext(const Model* model, const Model::Subgraph* subgraph,
+ flatbuffers::FlatBufferBuilder* builder,
+ std::vector<OperatorCodeFlatbuffer>* opCodesVector,
+ std::vector<int>* opCodeIndexForOperationType,
+ std::vector<BufferFlatbuffer>* bufferVector)
+ : mModel(model),
+ mSubgraph(subgraph),
+ mBuilder(builder),
+ mOpCodesVector(opCodesVector),
+ mOpCodeIndexForOperationType(opCodeIndexForOperationType),
+ mBufferVector(bufferVector) {
+ CHECK(model != nullptr);
+ CHECK(subgraph != nullptr);
+ CHECK(opCodesVector != nullptr);
+ CHECK(opCodeIndexForOperationType != nullptr);
+ CHECK(bufferVector != nullptr);
+
+ mOperandToTensorIdx.resize(subgraph->operands.size(), -1);
+ mMappings.resize(model->pools.size());
+}
+
+SubGraphFlatbuffer SubGraphContext::finish() {
+ return tflite::CreateSubGraphDirect(*mBuilder, &mTensorVector, &mInputTensors, &mOutputTensors,
+ &mOperatorVector);
+}
+
+int SubGraphContext::addTensorFlatbuffer(TensorFlatbuffer tensor, int32_t operandIdx) {
+ mTensorVector.push_back(tensor);
+
+ int tensorIdx = mTensorVector.size() - 1;
+ if (operandIdx >= 0) {
+ CHECK(mOperandToTensorIdx[operandIdx] == -1);
+ mOperandToTensorIdx[operandIdx] = tensorIdx;
+ }
+ return tensorIdx;
+}
+
+void SubGraphContext::addOperatorFlatbuffer(OperatorFlatbuffer opFlatbuffer) {
+ mOperatorVector.push_back(opFlatbuffer);
+}
+
+void SubGraphContext::addSubGraphInput(int32_t operandIdx) {
+ CHECK(mOperandToTensorIdx[operandIdx] != -1);
+ mInputTensors.push_back(mOperandToTensorIdx[operandIdx]);
+}
+
+void SubGraphContext::addSubGraphOutput(int32_t operandIdx) {
+ CHECK(mOperandToTensorIdx[operandIdx] != -1);
+ mOutputTensors.push_back(mOperandToTensorIdx[operandIdx]);
+}
+
+uint32_t SubGraphContext::addOpCode(OperationType operationType) {
+ uint32_t idx = static_cast<uint32_t>(operationType);
+ if (mOpCodeIndexForOperationType->at(idx) != -1) {
+ return mOpCodeIndexForOperationType->at(idx);
+ }
+
+ OperatorCodeFlatbuffer opCode;
+
+ tflite::BuiltinOperator builtinCode = getFlatbufferOperator(operationType);
+ if (builtinCode < tflite::BuiltinOperator::BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES)
+ opCode = tflite::CreateOperatorCode(
+ *mBuilder, static_cast<int8_t>(builtinCode) /* deprecated_builtin_code */,
+ 0 /* custom_code */, getMaxOperatorVersionCode(builtinCode) /* version */);
+ else
+ opCode = tflite::CreateOperatorCode(*mBuilder, 0 /* deprecated_builtin_code */,
+ 0 /* custom_code */,
+ getMaxOperatorVersionCode(builtinCode) /* version */,
+ builtinCode /* builtin_code */);
+
+ mOpCodesVector->push_back(opCode);
+ uint32_t opCodeIdx = mOpCodesVector->size() - 1;
+ (*mOpCodeIndexForOperationType)[idx] = opCodeIdx;
+ return opCodeIdx;
+}
+
+int SubGraphContext::getTensorIdxFromOperandIdx(int operandIdx) const {
+ return mOperandToTensorIdx[operandIdx];
+}
+
+const Mapping& SubGraphContext::getMapping(uint32_t poolIndex) {
+ if (mMappings[poolIndex].size > 0) {
+ return mMappings[poolIndex];
+ }
+
+ SharedMemory memory = mModel->pools[poolIndex];
+ GeneralResult<Mapping> mapping = map(memory);
+ CHECK(mapping.has_value()) << "CONSTANT_REFERENCE memory mapping error: "
+ << mapping.error().message;
+
+ mMappings[poolIndex] = std::move(mapping).value();
+ return mMappings[poolIndex];
+}
+
+std::pair<const uint8_t*, uint32_t> SubGraphContext::getConstantPointerAndLength(
+ const Operand& operand) {
+ CHECK(isOperandConstant(operand));
+
+ if (operand.lifetime == Operand::LifeTime::CONSTANT_COPY) {
+ return std::make_pair(mModel->operandValues.data() + operand.location.offset,
+ operand.location.length);
+ }
+
+ const Mapping& mapping = getMapping(operand.location.poolIndex);
+ const uint8_t* memoryPtr = static_cast<const uint8_t*>(
+ std::visit([](auto ptr) { return static_cast<const void*>(ptr); }, mapping.pointer));
+
+ return std::make_pair(memoryPtr + operand.location.offset, operand.location.length);
+}
+
+uint32_t SubGraphContext::addBufferFromData(const uint8_t* data, uint32_t length) {
+ auto dataVectorFlatbuffer = mBuilder->CreateVector(data, length);
+
+ auto buffer = tflite::CreateBuffer(*mBuilder, dataVectorFlatbuffer);
+ mBufferVector->push_back(buffer);
+
+ return mBufferVector->size() - 1;
+}
+
+Result<void> SubGraphContext::createTensorFlatbufferFromOperand(uint32_t operandIdx,
+ bool makeSymmetric) {
+ // An output Operand to one Operation can be an input Operand to
+ // another Operation, so this function can be run more than once.
+ // We simply return if the Tensor for the Operand is already created.
+ if (mOperandToTensorIdx[operandIdx] != -1) return {};
+
+ const Operand& operand = mSubgraph->operands[operandIdx];
+
+ std::vector<float> scaleVector{operand.scale};
+ std::vector<int64_t> zeroPointVector{operand.zeroPoint};
+ // min and max used to convert TFLite models to TF models, so it is unused in this case and can
+ // be set to 0
+ std::vector<float> minVector{0};
+ std::vector<float> maxVector{0};
+
+ // build quantization parameters
+ auto quantizationParams = tflite::CreateQuantizationParametersDirect(
+ *mBuilder, &minVector /* min */, &maxVector /* max */, &scaleVector /* scale */,
+ &zeroPointVector /* zero_point */,
+ tflite::QuantizationDetails::QuantizationDetails_NONE /* details_type */);
+
+ // add buffer if constant operand
+ // buffer at index 0 is reserved for tensors without a buffer
+ uint32_t bufferIdx = 0;
+ if (isOperandConstant(operand)) {
+ auto [data, dataLength] = getConstantPointerAndLength(operand);
+ if (makeSymmetric && operand.type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) {
+ std::vector<int8_t> dataVector(reinterpret_cast<const int8_t*>(data),
+ reinterpret_cast<const int8_t*>(data) + dataLength);
+ bool emitWarning = false;
+ for (uint32_t i = 0; i < dataLength; i++) {
+ int32_t newValue = static_cast<int32_t>(dataVector[i]) - operand.zeroPoint;
+ if (newValue < std::numeric_limits<int8_t>::min() ||
+ newValue > std::numeric_limits<int8_t>::max()) {
+ emitWarning = true;
+ }
+ dataVector[i] = static_cast<int8_t>(std::clamp(
+ newValue, static_cast<int32_t>(std::numeric_limits<int8_t>::min()),
+ static_cast<int32_t>(std::numeric_limits<int8_t>::max())));
+ }
+
+ if (emitWarning) {
+ LOG(WARNING) << "Asymmetric to symmetric conversion will result in "
+ "underflow/overflow. Clamping data";
+ }
+ bufferIdx = addBufferFromData(reinterpret_cast<const uint8_t*>(dataVector.data()),
+ dataLength);
+ } else {
+ bufferIdx = addBufferFromData(data, dataLength);
+ }
+ }
+
+ // shape of tensor
+ std::vector<int32_t> shape(operand.dimensions.begin(), operand.dimensions.end());
+ replaceZeroDimensions(&shape);
+
+ // build tensor
+ TensorFlatbuffer tensor = tflite::CreateTensorDirect(
+ *mBuilder, &shape, NN_TRY(getTensorFlatbufferOperandType(operand.type)) /* type */,
+ bufferIdx /* buffer */, 0 /* name */, quantizationParams /* quantization */);
+ addTensorFlatbuffer(tensor, operandIdx);
+
+ return {};
+}
+
+} // namespace nn
+} // namespace android
\ No newline at end of file
diff --git a/runtime/operation_converters/SubGraphContext.h b/runtime/operation_converters/SubGraphContext.h
new file mode 100644
index 0000000..17d3d0e
--- /dev/null
+++ b/runtime/operation_converters/SubGraphContext.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_SUBGRAPH_CONTEXT_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_SUBGRAPH_CONTEXT_H
+
+#include <utility>
+#include <vector>
+
+#include "FlatbufferModelBuilderUtils.h"
+#include "NeuralNetworks.h"
+
+namespace android {
+namespace nn {
+
+// This keeps track of all the data needed to convert NNAPI subgraphs to TFLite subgraphs
+// This also provides information needed to convert NNAPI Operations to TFLite Operators
+// Once the subgraph is done building, call finish() to return the flatbuffer
+class SubGraphContext {
+ public:
+ SubGraphContext(const Model* model, const Model::Subgraph* subgraph,
+ flatbuffers::FlatBufferBuilder* builder,
+ std::vector<OperatorCodeFlatbuffer>* opCodesVector,
+ std::vector<int>* opCodeIndexForOperationType,
+ std::vector<BufferFlatbuffer>* bufferVector);
+
+ SubGraphFlatbuffer finish();
+
+ // If the operandIdx is -1, it suggests that the tensor being added doesn't have a
+ // corresponding Operand from the NNAPI NDK model.
+ // Returns index of Tensor being added.
+ int addTensorFlatbuffer(TensorFlatbuffer tensor, int32_t operandIdx = -1);
+ void addOperatorFlatbuffer(OperatorFlatbuffer opFlatbuffer);
+ void addSubGraphInput(int32_t operandIdx);
+ void addSubGraphOutput(int32_t operandIdx);
+
+ const Model::Subgraph* getSubgraph() const { return mSubgraph; }
+ // Returns -1 if there is no corresponding tensor index
+ int getTensorIdxFromOperandIdx(int operandIdx) const;
+ uint32_t addOpCode(OperationType operationType);
+ flatbuffers::FlatBufferBuilder& getBuilder() { return *mBuilder; }
+
+ // OperandLifeTime must be CONSTANT_COPY or CONSTANT_REFERENCE
+ // Will crash if OperandLifeTime is not either of the two.
+ // dataSize is the size of data in bytes.
+ template <typename Type>
+ void copyConstantValueToData(const Operand& operand, Type* data, size_t dataSize);
+ template <typename Type>
+ Type getConstantScalar(const Operand& operand);
+
+ // Returns Buffer index
+ uint32_t addBufferFromData(const uint8_t* data, uint32_t length);
+ // makeSymmetric turns asymmetric tensors to symmetric by doing setting data = data - zeroPoint
+ // makeSymmetric is supported only for constant OperandType::TENSOR_QUANT8_ASYMM_SIGNED
+ // If unsupported type is passed, makeSymmetric is ignored
+ Result<void> createTensorFlatbufferFromOperand(uint32_t operandIdx, bool makeSymmetric = false);
+
+ private:
+ const Mapping& getMapping(uint32_t poolIndex);
+ std::pair<const uint8_t*, uint32_t> getConstantPointerAndLength(const Operand& operand);
+
+ const Model* mModel;
+ const Model::Subgraph* mSubgraph;
+ flatbuffers::FlatBufferBuilder* mBuilder;
+
+ std::vector<OperatorCodeFlatbuffer>* mOpCodesVector;
+ std::vector<int>* mOpCodeIndexForOperationType;
+ std::vector<BufferFlatbuffer>* mBufferVector;
+
+ std::vector<OperatorFlatbuffer> mOperatorVector;
+ std::vector<TensorFlatbuffer> mTensorVector;
+ std::vector<int32_t> mInputTensors;
+ std::vector<int32_t> mOutputTensors;
+ std::vector<int> mOperandToTensorIdx;
+ // Each index corresponds to the pool index of shared memory
+ std::vector<Mapping> mMappings;
+};
+
+template <typename Type>
+void SubGraphContext::copyConstantValueToData(const Operand& operand, Type* data, size_t dataSize) {
+ auto [pointer, length] = getConstantPointerAndLength(operand);
+ CHECK_GE(dataSize, length);
+
+ std::memcpy(data, pointer, length);
+}
+
+template <typename Type>
+Type SubGraphContext::getConstantScalar(const Operand& operand) {
+ Type data;
+ copyConstantValueToData(operand, &data, sizeof(Type));
+ return data;
+}
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_OPERATION_CONVERTERS_SUBGRAPH_CONTEXT_H
\ No newline at end of file
diff --git a/runtime/test/Android.bp b/runtime/test/Android.bp
index 49b1e30..bcdb10d 100644
--- a/runtime/test/Android.bp
+++ b/runtime/test/Android.bp
@@ -201,6 +201,58 @@
],
}
+cc_defaults {
+ name: "NeuralNetworksTest_v2_static_defaults",
+ defaults: ["NeuralNetworksTest_static_defaults"],
+ srcs: [
+ "TestCompatibilityLayer.cpp",
+ ],
+ exclude_srcs: [
+ "PreparedModelCallback.cpp",
+ "TestCompilationCaching.cpp",
+ "TestCompliance.cpp",
+ "TestControlFlow.cpp",
+ "TestExecution.cpp",
+ "TestExtensions.cpp",
+ "TestFailingDriver.cpp",
+ "TestFree.cpp",
+ "TestGenerated.cpp",
+ "TestIntrospectionControl.cpp",
+ "TestMemory.cpp",
+ "TestMemoryDomain.cpp",
+ "TestMemoryInternal.cpp",
+ "TestOperandExtraParams.cpp",
+ "TestPartitioning.cpp",
+ "TestPartitioningRandom.cpp",
+ "TestRemoveDefaultArguments.cpp",
+ "TestServerFlag.cpp",
+ "TestTelemetry.cpp",
+ "TestTrivialModel.cpp",
+ "TestUnknownDimensions.cpp",
+ "TestUnspecifiedDimensions.cpp",
+ "TestUpdatability.cpp",
+ "TestValidateModel.cpp",
+ "TestValidateOperations.cpp",
+ "TestValidation.cpp",
+ "fibonacci_extension/FibonacciDriver.cpp",
+ "fibonacci_extension/FibonacciExtensionTest.cpp",
+ ],
+
+ include_dirs: [
+ "external/flatbuffers/include",
+ "external/tensorflow",
+ ],
+
+ static_libs: [
+ "libflatbuffers-cpp",
+ "libneuralnetworks_v2_static_experimental",
+ "libtflite_static",
+ ],
+ exclude_static_libs: [
+ "libneuralnetworks_static",
+ ],
+}
+
cc_test {
name: "NeuralNetworksTest_static",
defaults: ["NeuralNetworksTest_static_defaults"],
@@ -238,6 +290,41 @@
},
}
+cc_test {
+ name: "NeuralNetworksTest_v2_static",
+ defaults: ["NeuralNetworksTest_v2_static_defaults"],
+ test_suites: [
+ "general-tests",
+ ],
+ target: {
+ android: {
+ test_config: "AndroidTest_NeuralNetworksTest_v2_static.xml",
+ srcs: ["TestStatsdTelemetry.cpp"],
+ },
+ host: {
+ cflags: [
+ "-D__ANDROID_API__=10000",
+ ],
+ },
+ },
+ whole_static_libs: [
+ "neuralnetworks_generated_experimental_example",
+ ],
+ exclude_static_libs: [
+ "libneuralnetworks_common",
+ "neuralnetworks_types",
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "libneuralnetworks_common_experimental",
+ "neuralnetworks_types_experimental",
+ ],
+ cflags: ["-DNN_EXPERIMENTAL_FEATURE"],
+ test_options: {
+ unit_test: false,
+ },
+}
+
tidy_disabled_operation_signatures_files = [
// These took too much time with clang-tidy.
"fuzzing/operation_signatures/Convolutions.cpp",
@@ -619,7 +706,6 @@
},
},
sanitize: {
- address: true,
all_undefined: true,
},
strip: {
diff --git a/runtime/test/AndroidTest_NeuralNetworksTest_v2_static.xml b/runtime/test/AndroidTest_NeuralNetworksTest_v2_static.xml
new file mode 100644
index 0000000..d0ca057
--- /dev/null
+++ b/runtime/test/AndroidTest_NeuralNetworksTest_v2_static.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs NeuralNetworksTest_v2_static.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="false" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="NeuralNetworksTest_v2_static->/data/local/tmp/NeuralNetworksTest_v2_static" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="NeuralNetworksTest_v2_static" />
+ <option name="native-test-timeout" value="3h" />
+ </test>
+</configuration>
diff --git a/runtime/test/TestCompatibilityLayer.cpp b/runtime/test/TestCompatibilityLayer.cpp
new file mode 100644
index 0000000..99674a6
--- /dev/null
+++ b/runtime/test/TestCompatibilityLayer.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <ftw.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include "AndroidVersionUtil.h"
+#include "GeneratedTestUtils.h"
+#include "NeuralNetworks.h"
+#include "NeuralNetworksTypes.h"
+#include "TestHarness.h"
+#include "TestNeuralNetworksWrapper.h"
+#include "TestUtils.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#include "tensorflow/lite/interpreter.h"
+#include "tensorflow/lite/kernels/register.h"
+#include "tensorflow/lite/model.h"
+#pragma clang diagnostic pop
+
+#ifdef NNTEST_CTS
+#define NNTEST_COMPUTE_MODE
+#endif
+
+namespace android::nn::generated_tests {
+using namespace test_wrapper;
+using namespace test_helper;
+
+class CompatibilityLayerGeneratedTests : public GeneratedTestBase {
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ // Test driver for those generated from packages/modules/NeuralNetworks/runtime/test/specs
+ void execute(const TestModel& testModel);
+
+ bool mTestDynamicOutputShape = false;
+ bool mTestSupported = true;
+};
+
+class CompatibilityLayerGeneratedTestsSupported : public CompatibilityLayerGeneratedTests {};
+class CompatibilityLayerGeneratedTestsUnsupported : public CompatibilityLayerGeneratedTests {};
+class CompatibilityLayerGeneratedTestsDynamicOutput : public CompatibilityLayerGeneratedTests {};
+
+void CompatibilityLayerGeneratedTests::execute(const TestModel& testModel) {
+ GeneratedModel model;
+ createModel(testModel, mTestDynamicOutputShape, &model);
+ if (testModel.expectFailure && !model.isValid()) {
+ return;
+ }
+ ASSERT_EQ(model.finish(), Result::NO_ERROR);
+ ASSERT_TRUE(model.isValid());
+
+ Compilation compilation(&model);
+ Result result = compilation.finish();
+ if (!mTestSupported && result != Result::NO_ERROR) return;
+ ASSERT_EQ(result, Result::NO_ERROR);
+
+ Execution execution(&compilation);
+
+ // Model inputs.
+ for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+ const auto& operand = testModel.main.operands[testModel.main.inputIndexes[i]];
+ ASSERT_EQ(Result::NO_ERROR,
+ execution.setInput(i, operand.data.get<void>(), operand.data.size()));
+ }
+
+ // Model outputs.
+ std::vector<TestBuffer> outputs;
+ for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
+ const auto& operand = testModel.main.operands[testModel.main.outputIndexes[i]];
+ const size_t bufferSize = std::max<size_t>(operand.data.size(), 1);
+ outputs.emplace_back(bufferSize);
+
+ ASSERT_EQ(Result::NO_ERROR,
+ execution.setOutput(i, outputs.back().getMutable<void>(), bufferSize));
+ }
+
+ result = execution.compute(Execution::ComputeMode::SYNC);
+ ASSERT_EQ(result, Result::NO_ERROR);
+
+ // If a conv filter under/overflows, "compatibleTest" will report
+ // unsupported, but the actual conversion will result in NO_ERROR because
+ // it is treated as a warning, rather than an error. Because of the accuracy
+ // loss, we should not check test results in such a case.
+ //
+ // TODO(b/237410741): A potentially better approach is to have
+ // "compatibleTest" report three status: fully supported, supported with
+ // accuracy loss, and not supported.
+ if (mTestSupported) {
+ checkResults(testModel, outputs);
+ }
+}
+
+void CompatibilityLayerGeneratedTests::SetUp() {
+ GeneratedTestBase::SetUp();
+}
+
+void CompatibilityLayerGeneratedTests::TearDown() {
+ GeneratedTestBase::TearDown();
+}
+
+namespace {
+
+bool compatibleTest(const TestModel& testModel) {
+ static const std::vector<TestOperationType> kSupportedOperationTypes{
+ TestOperationType::CONV_2D, TestOperationType::ADD,
+ TestOperationType::DEPTHWISE_CONV_2D, TestOperationType::LOGISTIC};
+ static const std::vector<TestOperandType> kSupportedOperandTypes{
+ TestOperandType::TENSOR_FLOAT32, TestOperandType::TENSOR_INT32,
+ TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED, TestOperandType::BOOL,
+ TestOperandType::INT32};
+
+ if (testModel.hasControlFlow()) {
+ return false;
+ }
+
+ bool result = true;
+ const TestSubgraph& mainSubgraph = testModel.main;
+
+ result &= std::all_of(
+ mainSubgraph.operations.begin(), mainSubgraph.operations.end(),
+ [&mainSubgraph](const TestOperation& operation) {
+ bool isOperationCompatible = true;
+ // ensure that tensors are nhwc and filter is constant
+ if (operation.type == TestOperationType::CONV_2D ||
+ operation.type == TestOperationType::DEPTHWISE_CONV_2D) {
+ size_t implicitIsNchwIdx =
+ (operation.type == TestOperationType::CONV_2D) ? 7 : 8;
+ size_t explicitIsNchwIdx = implicitIsNchwIdx + 3;
+ bool isImplicitPadding =
+ operation.inputs.size() <= implicitIsNchwIdx ||
+ mainSubgraph.operands[operation.inputs[implicitIsNchwIdx]].type ==
+ TestOperandType::BOOL;
+ size_t isNchwIdx = isImplicitPadding ? implicitIsNchwIdx : explicitIsNchwIdx;
+
+ if (operation.inputs.size() > static_cast<uint32_t>(isNchwIdx)) {
+ isOperationCompatible &=
+ !(*mainSubgraph.operands[operation.inputs[isNchwIdx]]
+ .data.get<bool>());
+ }
+
+ const int kFilterIdx = 1;
+ const TestOperand& filterOperand =
+ mainSubgraph.operands[operation.inputs[kFilterIdx]];
+ TestOperandLifeTime filterLifetime = filterOperand.lifetime;
+ isOperationCompatible &=
+ (filterLifetime == TestOperandLifeTime::CONSTANT_COPY) ||
+ (filterLifetime == TestOperandLifeTime::CONSTANT_REFERENCE);
+
+ // check that making filter operands symmetrical does not over/underflow
+ // this is because the outputs of the model will be different from expected if
+ // the operand value changes with the under/overflow
+ if (filterOperand.type == TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED) {
+ const int8_t* data = filterOperand.data.get<int8_t>();
+ size_t dataSize = filterOperand.data.size();
+
+ for (int32_t i = 0; i < static_cast<int32_t>(dataSize); i++) {
+ int32_t newValue =
+ static_cast<int32_t>(data[i]) - filterOperand.zeroPoint;
+ if (newValue < std::numeric_limits<int8_t>::min() ||
+ newValue > std::numeric_limits<int8_t>::max()) {
+ isOperationCompatible = false;
+ break;
+ }
+ }
+ }
+ }
+
+ isOperationCompatible &=
+ std::find(kSupportedOperationTypes.begin(), kSupportedOperationTypes.end(),
+ operation.type) != kSupportedOperationTypes.end();
+
+ return isOperationCompatible;
+ });
+
+ result &= std::all_of(mainSubgraph.operands.begin(), mainSubgraph.operands.end(),
+ [](const TestOperand& operand) {
+ return std::find(kSupportedOperandTypes.begin(),
+ kSupportedOperandTypes.end(),
+ operand.type) != kSupportedOperandTypes.end();
+ });
+
+ return result;
+}
+
+} // namespace
+
+TEST_P(CompatibilityLayerGeneratedTestsSupported, CompatibilityLayerSupported) {
+ mTestSupported = true;
+ execute(testModel);
+}
+
+TEST_P(CompatibilityLayerGeneratedTestsUnsupported, CompatibilityLayerUnsupported) {
+ mTestSupported = false;
+ execute(testModel);
+}
+
+TEST_P(CompatibilityLayerGeneratedTestsDynamicOutput, CompatibilityLayerDynamicOutput) {
+ mTestDynamicOutputShape = true;
+ mTestSupported = false;
+ execute(testModel);
+}
+
+INSTANTIATE_GENERATED_TEST(CompatibilityLayerGeneratedTestsSupported,
+ [](const TestModel& testModel) {
+ return !testModel.expectFailure && compatibleTest(testModel);
+ });
+
+INSTANTIATE_GENERATED_TEST(CompatibilityLayerGeneratedTestsUnsupported,
+ [](const TestModel& testModel) {
+ return !testModel.expectFailure && !compatibleTest(testModel);
+ });
+
+INSTANTIATE_GENERATED_TEST(CompatibilityLayerGeneratedTestsDynamicOutput,
+ [](const TestModel& testModel) {
+ return !testModel.expectFailure && !testModel.hasScalarOutputs();
+ });
+
+} // namespace android::nn::generated_tests
diff --git a/shim_and_sl/ShimConverter.cpp b/shim_and_sl/ShimConverter.cpp
index ed3cda2..9914af1 100644
--- a/shim_and_sl/ShimConverter.cpp
+++ b/shim_and_sl/ShimConverter.cpp
@@ -154,6 +154,10 @@
break;
}
case OperandLifeTime::CONSTANT_POOL: {
+ if (operand.location.poolIndex >= memoryPools.size()) {
+ *errorStatus = ErrorStatus::INVALID_ARGUMENT;
+ return nullptr;
+ }
resultModel.setOperandValueFromMemory(
i, memoryPools[operand.location.poolIndex].get(), operand.location.offset,
operand.location.length);
diff --git a/tools/test_generator/test_harness/include/TestHarness.h b/tools/test_generator/test_harness/include/TestHarness.h
index 743af80..d702c2a 100644
--- a/tools/test_generator/test_harness/include/TestHarness.h
+++ b/tools/test_generator/test_harness/include/TestHarness.h
@@ -366,6 +366,8 @@
return newTestModel;
}
+ bool hasControlFlow() const { return !referenced.empty(); }
+
bool hasQuant8CoupledOperands() const {
bool result = false;
forEachSubgraph([&result](const TestSubgraph& subgraph) {