Implement NNAPI canonical interfaces
This CL implements the canonical IDevice, IPreparedModel, and IBuffer
interfaces for the 1.0, 1.1, 1.2, and 1.3 NN HIDL HAL interfaces.
Further, it introduces "Resilient" adapter interfaces to automatically
retrieve a handle to a recovered interface object after it has died and
rebooted.
This CL also updates the conversion code from returning nn::Result to
nn::GeneralResult, which includes a ErrorStatus code in the case of an
error.
Finally, this CL introduces a new static library
neuralnetworks_utils_hal_service which consists of a single function
::android::nn::hal::getDevices which can be used by the NNAPI runtime to
retrieve the HIDL services without knowing the underlying HIDL types.
Bug: 160668438
Test: mma
Test: NeuralNetworksTest_static
Change-Id: Iec6ae739df196b4034ffb35ea76781fd541ffec3
Merged-In: Iec6ae739df196b4034ffb35ea76781fd541ffec3
(cherry picked from commit 3670c385c4b12aef975ab67e5d2b0f5fe79134c2)
diff --git a/neuralnetworks/1.2/utils/src/Callbacks.cpp b/neuralnetworks/1.2/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..cb739f0
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Callbacks.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <utility>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
+ const sp<V1_0::IPreparedModel>& preparedModel) {
+ return NN_TRY(V1_0::utils::PreparedModel::create(preparedModel));
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
+ const sp<IPreparedModel>& preparedModel) {
+ return NN_TRY(utils::PreparedModel::create(preparedModel));
+}
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionGeneralResultsHelper(const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ return std::make_pair(NN_TRY(validatedConvertToCanonical(outputShapes)),
+ NN_TRY(validatedConvertToCanonical(timing)));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionGeneralResults(const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+ return hal::utils::makeExecutionFailure(
+ convertExecutionGeneralResultsHelper(outputShapes, timing));
+}
+
+} // namespace
+
+Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
+ const sp<V1_0::IPreparedModel>& preparedModel) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
+ } else if (preparedModel == nullptr) {
+ notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Returned preparedModel is nullptr");
+ } else {
+ notifyInternal(convertPreparedModel(preparedModel));
+ }
+ return Void();
+}
+
+Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
+ const sp<IPreparedModel>& preparedModel) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
+ } else if (preparedModel == nullptr) {
+ notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Returned preparedModel is nullptr");
+ } else {
+ notifyInternal(convertPreparedModel(preparedModel));
+ }
+ return Void();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+ notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+ return mData.take();
+}
+
+void PreparedModelCallback::notifyInternal(PreparedModelCallback::Data result) {
+ mData.put(std::move(result));
+}
+
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
+ } else {
+ notifyInternal({});
+ }
+ return Void();
+}
+
+Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus status,
+ const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
+ } else {
+ notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
+ }
+ return Void();
+}
+
+void ExecutionCallback::notifyAsDeadObject() {
+ notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+ExecutionCallback::Data ExecutionCallback::get() {
+ return mData.take();
+}
+
+void ExecutionCallback::notifyInternal(ExecutionCallback::Data result) {
+ mData.put(std::move(result));
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index fed314b..378719a 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -26,6 +26,7 @@
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
#include <algorithm>
#include <functional>
@@ -78,7 +79,7 @@
using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
template <typename Type>
-Result<std::vector<ConvertOutput<Type>>> convertVec(const hidl_vec<Type>& arguments) {
+GeneralResult<std::vector<ConvertOutput<Type>>> convertVec(const hidl_vec<Type>& arguments) {
std::vector<ConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
@@ -88,25 +89,25 @@
}
template <typename Type>
-Result<std::vector<ConvertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
+GeneralResult<std::vector<ConvertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
return convertVec(arguments);
}
} // anonymous namespace
-Result<OperandType> convert(const hal::V1_2::OperandType& operandType) {
+GeneralResult<OperandType> convert(const hal::V1_2::OperandType& operandType) {
return static_cast<OperandType>(operandType);
}
-Result<OperationType> convert(const hal::V1_2::OperationType& operationType) {
+GeneralResult<OperationType> convert(const hal::V1_2::OperationType& operationType) {
return static_cast<OperationType>(operationType);
}
-Result<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
+GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
return static_cast<DeviceType>(deviceType);
}
-Result<Capabilities> convert(const hal::V1_2::Capabilities& capabilities) {
+GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities) {
const bool validOperandTypes = std::all_of(
capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
[](const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
@@ -114,7 +115,7 @@
return !maybeType.has_value() ? false : validOperandType(maybeType.value());
});
if (!validOperandTypes) {
- return NN_ERROR()
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
<< "Invalid OperandType when converting OperandPerformance in Capabilities";
}
@@ -124,8 +125,9 @@
NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor));
auto operandPerformance = NN_TRY(convert(capabilities.operandPerformance));
- auto table =
- NN_TRY(Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)));
+ auto table = NN_TRY(hal::utils::makeGeneralFailure(
+ Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
+ nn::ErrorStatus::GENERAL_FAILURE));
return Capabilities{
.relaxedFloat32toFloat16PerformanceScalar = relaxedFloat32toFloat16PerformanceScalar,
@@ -134,7 +136,7 @@
};
}
-Result<Capabilities::OperandPerformance> convert(
+GeneralResult<Capabilities::OperandPerformance> convert(
const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
return Capabilities::OperandPerformance{
.type = NN_TRY(convert(operandPerformance.type)),
@@ -142,7 +144,7 @@
};
}
-Result<Operation> convert(const hal::V1_2::Operation& operation) {
+GeneralResult<Operation> convert(const hal::V1_2::Operation& operation) {
return Operation{
.type = NN_TRY(convert(operation.type)),
.inputs = operation.inputs,
@@ -150,7 +152,7 @@
};
}
-Result<Operand::SymmPerChannelQuantParams> convert(
+GeneralResult<Operand::SymmPerChannelQuantParams> convert(
const hal::V1_2::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
return Operand::SymmPerChannelQuantParams{
.scales = symmPerChannelQuantParams.scales,
@@ -158,7 +160,7 @@
};
}
-Result<Operand> convert(const hal::V1_2::Operand& operand) {
+GeneralResult<Operand> convert(const hal::V1_2::Operand& operand) {
return Operand{
.type = NN_TRY(convert(operand.type)),
.dimensions = operand.dimensions,
@@ -170,7 +172,7 @@
};
}
-Result<Operand::ExtraParams> convert(const hal::V1_2::Operand::ExtraParams& extraParams) {
+GeneralResult<Operand::ExtraParams> convert(const hal::V1_2::Operand::ExtraParams& extraParams) {
using Discriminator = hal::V1_2::Operand::ExtraParams::hidl_discriminator;
switch (extraParams.getDiscriminator()) {
case Discriminator::none:
@@ -180,11 +182,12 @@
case Discriminator::extension:
return extraParams.extension();
}
- return NN_ERROR() << "Unrecognized Operand::ExtraParams discriminator: "
- << underlyingType(extraParams.getDiscriminator());
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Unrecognized Operand::ExtraParams discriminator: "
+ << underlyingType(extraParams.getDiscriminator());
}
-Result<Model> convert(const hal::V1_2::Model& model) {
+GeneralResult<Model> convert(const hal::V1_2::Model& model) {
auto operations = NN_TRY(convert(model.operations));
// Verify number of consumers.
@@ -193,9 +196,9 @@
CHECK(model.operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < model.operands.size(); ++i) {
if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
- return NN_ERROR() << "Invalid numberOfConsumers for operand " << i << ", expected "
- << numberOfConsumers[i] << " but found "
- << model.operands[i].numberOfConsumers;
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Invalid numberOfConsumers for operand " << i << ", expected "
+ << numberOfConsumers[i] << " but found " << model.operands[i].numberOfConsumers;
}
}
@@ -215,7 +218,7 @@
};
}
-Result<Model::ExtensionNameAndPrefix> convert(
+GeneralResult<Model::ExtensionNameAndPrefix> convert(
const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
return Model::ExtensionNameAndPrefix{
.name = extensionNameAndPrefix.name,
@@ -223,29 +226,29 @@
};
}
-Result<OutputShape> convert(const hal::V1_2::OutputShape& outputShape) {
+GeneralResult<OutputShape> convert(const hal::V1_2::OutputShape& outputShape) {
return OutputShape{
.dimensions = outputShape.dimensions,
.isSufficient = outputShape.isSufficient,
};
}
-Result<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming) {
+GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming) {
return static_cast<MeasureTiming>(measureTiming);
}
-Result<Timing> convert(const hal::V1_2::Timing& timing) {
+GeneralResult<Timing> convert(const hal::V1_2::Timing& timing) {
return Timing{.timeOnDevice = timing.timeOnDevice, .timeInDriver = timing.timeInDriver};
}
-Result<Extension> convert(const hal::V1_2::Extension& extension) {
+GeneralResult<Extension> convert(const hal::V1_2::Extension& extension) {
return Extension{
.name = extension.name,
.operandTypes = NN_TRY(convert(extension.operandTypes)),
};
}
-Result<Extension::OperandTypeInformation> convert(
+GeneralResult<Extension::OperandTypeInformation> convert(
const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation) {
return Extension::OperandTypeInformation{
.type = operandTypeInformation.type,
@@ -254,20 +257,21 @@
};
}
-Result<NativeHandle> convert(const hidl_handle& handle) {
+GeneralResult<NativeHandle> convert(const hidl_handle& handle) {
auto* cloned = native_handle_clone(handle.getNativeHandle());
return ::android::NativeHandle::create(cloned, /*ownsHandle=*/true);
}
-Result<std::vector<Extension>> convert(const hidl_vec<hal::V1_2::Extension>& extensions) {
+GeneralResult<std::vector<Extension>> convert(const hidl_vec<hal::V1_2::Extension>& extensions) {
return convertVec(extensions);
}
-Result<std::vector<NativeHandle>> convert(const hidl_vec<hidl_handle>& handles) {
+GeneralResult<std::vector<NativeHandle>> convert(const hidl_vec<hidl_handle>& handles) {
return convertVec(handles);
}
-Result<std::vector<OutputShape>> convert(const hidl_vec<hal::V1_2::OutputShape>& outputShapes) {
+GeneralResult<std::vector<OutputShape>> convert(
+ const hidl_vec<hal::V1_2::OutputShape>& outputShapes) {
return convertVec(outputShapes);
}
@@ -278,24 +282,24 @@
using utils::convert;
-nn::Result<V1_0::OperandLifeTime> convert(const nn::Operand::LifeTime& lifetime) {
+nn::GeneralResult<V1_0::OperandLifeTime> convert(const nn::Operand::LifeTime& lifetime) {
return V1_0::utils::convert(lifetime);
}
-nn::Result<V1_0::PerformanceInfo> convert(
+nn::GeneralResult<V1_0::PerformanceInfo> convert(
const nn::Capabilities::PerformanceInfo& performanceInfo) {
return V1_0::utils::convert(performanceInfo);
}
-nn::Result<V1_0::DataLocation> convert(const nn::DataLocation& location) {
+nn::GeneralResult<V1_0::DataLocation> convert(const nn::DataLocation& location) {
return V1_0::utils::convert(location);
}
-nn::Result<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
+nn::GeneralResult<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
return V1_0::utils::convert(operandValues);
}
-nn::Result<hidl_memory> convert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
return V1_0::utils::convert(memory);
}
@@ -303,7 +307,7 @@
using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
template <typename Type>
-nn::Result<hidl_vec<ConvertOutput<Type>>> convertVec(const std::vector<Type>& arguments) {
+nn::GeneralResult<hidl_vec<ConvertOutput<Type>>> convertVec(const std::vector<Type>& arguments) {
hidl_vec<ConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(convert(arguments[i]));
@@ -312,22 +316,23 @@
}
template <typename Type>
-nn::Result<hidl_vec<ConvertOutput<Type>>> convert(const std::vector<Type>& arguments) {
+nn::GeneralResult<hidl_vec<ConvertOutput<Type>>> convert(const std::vector<Type>& arguments) {
return convertVec(arguments);
}
-nn::Result<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
return Operand::ExtraParams{};
}
-nn::Result<Operand::ExtraParams> makeExtraParams(
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(
const nn::Operand::SymmPerChannelQuantParams& channelQuant) {
Operand::ExtraParams ret;
ret.channelQuant(NN_TRY(convert(channelQuant)));
return ret;
}
-nn::Result<Operand::ExtraParams> makeExtraParams(const nn::Operand::ExtensionParams& extension) {
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(
+ const nn::Operand::ExtensionParams& extension) {
Operand::ExtraParams ret;
ret.extension(extension);
return ret;
@@ -335,28 +340,29 @@
} // anonymous namespace
-nn::Result<OperandType> convert(const nn::OperandType& operandType) {
+nn::GeneralResult<OperandType> convert(const nn::OperandType& operandType) {
return static_cast<OperandType>(operandType);
}
-nn::Result<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType) {
return static_cast<OperationType>(operationType);
}
-nn::Result<DeviceType> convert(const nn::DeviceType& deviceType) {
+nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
switch (deviceType) {
case nn::DeviceType::UNKNOWN:
- return NN_ERROR() << "Invalid DeviceType UNKNOWN";
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Invalid DeviceType UNKNOWN";
case nn::DeviceType::OTHER:
case nn::DeviceType::CPU:
case nn::DeviceType::GPU:
case nn::DeviceType::ACCELERATOR:
return static_cast<DeviceType>(deviceType);
}
- return NN_ERROR() << "Invalid DeviceType " << underlyingType(deviceType);
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Invalid DeviceType " << underlyingType(deviceType);
}
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
std::vector<nn::Capabilities::OperandPerformance> operandPerformance;
operandPerformance.reserve(capabilities.operandPerformance.asVector().size());
std::copy_if(capabilities.operandPerformance.asVector().begin(),
@@ -375,7 +381,7 @@
};
}
-nn::Result<Capabilities::OperandPerformance> convert(
+nn::GeneralResult<Capabilities::OperandPerformance> convert(
const nn::Capabilities::OperandPerformance& operandPerformance) {
return Capabilities::OperandPerformance{
.type = NN_TRY(convert(operandPerformance.type)),
@@ -383,7 +389,7 @@
};
}
-nn::Result<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> convert(const nn::Operation& operation) {
return Operation{
.type = NN_TRY(convert(operation.type)),
.inputs = operation.inputs,
@@ -391,7 +397,7 @@
};
}
-nn::Result<SymmPerChannelQuantParams> convert(
+nn::GeneralResult<SymmPerChannelQuantParams> convert(
const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
return SymmPerChannelQuantParams{
.scales = symmPerChannelQuantParams.scales,
@@ -399,7 +405,7 @@
};
}
-nn::Result<Operand> convert(const nn::Operand& operand) {
+nn::GeneralResult<Operand> convert(const nn::Operand& operand) {
return Operand{
.type = NN_TRY(convert(operand.type)),
.dimensions = operand.dimensions,
@@ -412,13 +418,14 @@
};
}
-nn::Result<Operand::ExtraParams> convert(const nn::Operand::ExtraParams& extraParams) {
+nn::GeneralResult<Operand::ExtraParams> convert(const nn::Operand::ExtraParams& extraParams) {
return std::visit([](const auto& x) { return makeExtraParams(x); }, extraParams);
}
-nn::Result<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> convert(const nn::Model& model) {
if (!hal::utils::hasNoPointerData(model)) {
- return NN_ERROR() << "Model cannot be converted because it contains pointer-based memory";
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "Model cannot be converted because it contains pointer-based memory";
}
auto operands = NN_TRY(convert(model.main.operands));
@@ -443,7 +450,7 @@
};
}
-nn::Result<Model::ExtensionNameAndPrefix> convert(
+nn::GeneralResult<Model::ExtensionNameAndPrefix> convert(
const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
return Model::ExtensionNameAndPrefix{
.name = extensionNameAndPrefix.name,
@@ -451,27 +458,27 @@
};
}
-nn::Result<OutputShape> convert(const nn::OutputShape& outputShape) {
+nn::GeneralResult<OutputShape> convert(const nn::OutputShape& outputShape) {
return OutputShape{.dimensions = outputShape.dimensions,
.isSufficient = outputShape.isSufficient};
}
-nn::Result<MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
+nn::GeneralResult<MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
return static_cast<MeasureTiming>(measureTiming);
}
-nn::Result<Timing> convert(const nn::Timing& timing) {
+nn::GeneralResult<Timing> convert(const nn::Timing& timing) {
return Timing{.timeOnDevice = timing.timeOnDevice, .timeInDriver = timing.timeInDriver};
}
-nn::Result<Extension> convert(const nn::Extension& extension) {
+nn::GeneralResult<Extension> convert(const nn::Extension& extension) {
return Extension{
.name = extension.name,
.operandTypes = NN_TRY(convert(extension.operandTypes)),
};
}
-nn::Result<Extension::OperandTypeInformation> convert(
+nn::GeneralResult<Extension::OperandTypeInformation> convert(
const nn::Extension::OperandTypeInformation& operandTypeInformation) {
return Extension::OperandTypeInformation{
.type = operandTypeInformation.type,
@@ -480,22 +487,22 @@
};
}
-nn::Result<hidl_handle> convert(const nn::NativeHandle& handle) {
+nn::GeneralResult<hidl_handle> convert(const nn::NativeHandle& handle) {
const auto hidlHandle = hidl_handle(handle->handle());
// Copy memory to force the native_handle_t to be copied.
auto copiedHandle = hidlHandle;
return copiedHandle;
}
-nn::Result<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions) {
+nn::GeneralResult<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions) {
return convertVec(extensions);
}
-nn::Result<hidl_vec<hidl_handle>> convert(const std::vector<nn::NativeHandle>& handles) {
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::NativeHandle>& handles) {
return convertVec(handles);
}
-nn::Result<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes) {
+nn::GeneralResult<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes) {
return convertVec(outputShapes);
}
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
new file mode 100644
index 0000000..ca236f1
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Device.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<std::string> initVersionString(V1_2::IDevice* device) {
+ CHECK(device != nullptr);
+
+ nn::GeneralResult<std::string> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "uninitialized";
+ const auto cb = [&result](V1_0::ErrorStatus status, const hidl_string& versionString) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ result = NN_ERROR(canonical) << "getVersionString failed with " << toString(status);
+ } else {
+ result = versionString;
+ }
+ };
+
+ const auto ret = device->getVersionString(cb);
+ NN_TRY(hal::utils::handleTransportError(ret));
+
+ return result;
+}
+
+nn::GeneralResult<nn::DeviceType> initDeviceType(V1_2::IDevice* device) {
+ CHECK(device != nullptr);
+
+ nn::GeneralResult<nn::DeviceType> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "uninitialized";
+ const auto cb = [&result](V1_0::ErrorStatus status, DeviceType deviceType) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ result = NN_ERROR(canonical) << "getDeviceType failed with " << toString(status);
+ } else {
+ result = nn::convert(deviceType);
+ }
+ };
+
+ const auto ret = device->getType(cb);
+ NN_TRY(hal::utils::handleTransportError(ret));
+
+ return result;
+}
+
+nn::GeneralResult<std::vector<nn::Extension>> initExtensions(V1_2::IDevice* device) {
+ CHECK(device != nullptr);
+
+ nn::GeneralResult<std::vector<nn::Extension>> result =
+ NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+ const auto cb = [&result](V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ result = NN_ERROR(canonical) << "getExtensions failed with " << toString(status);
+ } else {
+ result = nn::convert(extensions);
+ }
+ };
+
+ const auto ret = device->getSupportedExtensions(cb);
+ NN_TRY(hal::utils::handleTransportError(ret));
+
+ return result;
+}
+
+nn::GeneralResult<nn::Capabilities> initCapabilities(V1_2::IDevice* device) {
+ CHECK(device != nullptr);
+
+ nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "uninitialized";
+ const auto cb = [&result](V1_0::ErrorStatus status, const Capabilities& capabilities) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ result = NN_ERROR(canonical) << "getCapabilities_1_2 failed with " << toString(status);
+ } else {
+ result = validatedConvertToCanonical(capabilities);
+ }
+ };
+
+ const auto ret = device->getCapabilities_1_2(cb);
+ NN_TRY(hal::utils::handleTransportError(ret));
+
+ return result;
+}
+
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> initNumberOfCacheFilesNeeded(
+ V1_2::IDevice* device) {
+ CHECK(device != nullptr);
+
+ nn::GeneralResult<std::pair<uint32_t, uint32_t>> result =
+ NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+ const auto cb = [&result](V1_0::ErrorStatus status, uint32_t numModelCache,
+ uint32_t numDataCache) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ result = NN_ERROR(canonical)
+ << "getNumberOfCacheFilesNeeded failed with " << toString(status);
+ } else {
+ result = std::make_pair(numModelCache, numDataCache);
+ }
+ };
+
+ const auto ret = device->getNumberOfCacheFilesNeeded(cb);
+ NN_TRY(hal::utils::handleTransportError(ret));
+
+ return result;
+}
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+ sp<V1_2::IDevice> device) {
+ if (name.empty()) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "V1_2::utils::Device::create must have non-empty name";
+ }
+ if (device == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "V1_2::utils::Device::create must have non-null device";
+ }
+
+ auto versionString = NN_TRY(initVersionString(device.get()));
+ const auto deviceType = NN_TRY(initDeviceType(device.get()));
+ auto extensions = NN_TRY(initExtensions(device.get()));
+ auto capabilities = NN_TRY(initCapabilities(device.get()));
+ const auto numberOfCacheFilesNeeded = NN_TRY(initNumberOfCacheFilesNeeded(device.get()));
+
+ auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+ return std::make_shared<const Device>(
+ PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
+ std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+ std::move(device), std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
+ nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+ nn::Capabilities capabilities,
+ std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, sp<V1_2::IDevice> device,
+ hal::utils::DeathHandler deathHandler)
+ : kName(std::move(name)),
+ kVersionString(std::move(versionString)),
+ kDeviceType(deviceType),
+ kExtensions(std::move(extensions)),
+ kCapabilities(std::move(capabilities)),
+ kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded),
+ kDevice(std::move(device)),
+ kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+ return kName;
+}
+
+const std::string& Device::getVersionString() const {
+ return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+ return nn::Version::ANDROID_Q;
+}
+
+nn::DeviceType Device::getType() const {
+ return kDeviceType;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+ return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+ return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+ return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> Device::wait() const {
+ const auto ret = kDevice->ping();
+ return hal::utils::handleTransportError(ret);
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+ // Ensure that model is ready for IPC.
+ std::optional<nn::Model> maybeModelInShared;
+ const nn::Model& modelInShared =
+ NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+ const auto hidlModel = NN_TRY(convert(modelInShared));
+
+ nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "uninitialized";
+ auto cb = [&result, &model](V1_0::ErrorStatus status,
+ const hidl_vec<bool>& supportedOperations) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ result = NN_ERROR(canonical)
+ << "getSupportedOperations_1_2 failed with " << toString(status);
+ } else if (supportedOperations.size() != model.main.operations.size()) {
+ result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "getSupportedOperations_1_2 returned vector of size "
+ << supportedOperations.size() << " but expected "
+ << model.main.operations.size();
+ } else {
+ result = supportedOperations;
+ }
+ };
+
+ const auto ret = kDevice->getSupportedOperations_1_2(hidlModel, cb);
+ NN_TRY(hal::utils::handleTransportError(ret));
+
+ return result;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+ const nn::Model& model, nn::ExecutionPreference preference, nn::Priority /*priority*/,
+ nn::OptionalTimePoint /*deadline*/, const std::vector<nn::NativeHandle>& modelCache,
+ const std::vector<nn::NativeHandle>& dataCache, const nn::CacheToken& token) const {
+ // Ensure that model is ready for IPC.
+ std::optional<nn::Model> maybeModelInShared;
+ const nn::Model& modelInShared =
+ NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+ const auto hidlModel = NN_TRY(convert(modelInShared));
+ const auto hidlPreference = NN_TRY(V1_1::utils::convert(preference));
+ const auto hidlModelCache = NN_TRY(convert(modelCache));
+ const auto hidlDataCache = NN_TRY(convert(dataCache));
+ const auto hidlToken = token;
+
+ const auto cb = sp<PreparedModelCallback>::make();
+ const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+ const auto ret = kDevice->prepareModel_1_2(hidlModel, hidlPreference, hidlModelCache,
+ hidlDataCache, hidlToken, cb);
+ const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ return NN_ERROR(canonical) << "prepareModel_1_2 failed with " << toString(status);
+ }
+
+ return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+ nn::OptionalTimePoint /*deadline*/, const std::vector<nn::NativeHandle>& modelCache,
+ const std::vector<nn::NativeHandle>& dataCache, const nn::CacheToken& token) const {
+ const auto hidlModelCache = NN_TRY(convert(modelCache));
+ const auto hidlDataCache = NN_TRY(convert(dataCache));
+ const auto hidlToken = token;
+
+ const auto cb = sp<PreparedModelCallback>::make();
+ const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+ const auto ret = kDevice->prepareModelFromCache(hidlModelCache, hidlDataCache, hidlToken, cb);
+ const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ return NN_ERROR(canonical) << "prepareModelFromCache failed with " << toString(status);
+ }
+
+ return cb->get();
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+ const nn::BufferDesc& /*desc*/,
+ const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+ const std::vector<nn::BufferRole>& /*inputRoles*/,
+ const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "IDevice::allocate not supported on 1.2 HAL service";
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..ff9db21
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PreparedModel.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionResultsHelper(const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+ return std::make_pair(NN_TRY(validatedConvertToCanonical(outputShapes)),
+ NN_TRY(validatedConvertToCanonical(timing)));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
+ const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+ return hal::utils::makeExecutionFailure(convertExecutionResultsHelper(outputShapes, timing));
+}
+
+} // namespace
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+ sp<V1_2::IPreparedModel> preparedModel) {
+ if (preparedModel == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "V1_2::utils::PreparedModel::create must have non-null preparedModel";
+ }
+
+ auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
+ return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
+ std::move(deathHandler));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_2::IPreparedModel> preparedModel,
+ hal::utils::DeathHandler deathHandler)
+ : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeSynchronously(const V1_0::Request& request, MeasureTiming measure) const {
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
+ NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+ const auto cb = [&result](V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
+ } else {
+ result = convertExecutionResults(outputShapes, timing);
+ }
+ };
+
+ const auto ret = kPreparedModel->executeSynchronously(request, measure, cb);
+ NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
+
+ return result;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeAsynchronously(const V1_0::Request& request, MeasureTiming measure) const {
+ const auto cb = sp<ExecutionCallback>::make();
+ const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+ const auto ret = kPreparedModel->execute_1_2(request, measure, cb);
+ const auto status =
+ NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
+ if (status != V1_0::ErrorStatus::NONE) {
+ const auto canonical =
+ validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+ return NN_ERROR(canonical) << "execute failed with " << toString(status);
+ }
+
+ return cb->get();
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/) const {
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
+ hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+
+ const auto hidlRequest =
+ NN_TRY(hal::utils::makeExecutionFailure(V1_0::utils::convert(requestInShared)));
+ const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
+ NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+ const bool preferSynchronous = true;
+
+ // Execute synchronously if allowed.
+ if (preferSynchronous) {
+ result = executeSynchronously(hidlRequest, hidlMeasure);
+ }
+
+ // Run asymchronous execution if execution has not already completed.
+ if (!result.has_value()) {
+ result = executeAsynchronously(hidlRequest, hidlMeasure);
+ }
+
+ // Flush output buffers if suxcessful execution.
+ if (result.has_value()) {
+ NN_TRY(hal::utils::makeExecutionFailure(
+ hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
+ }
+
+ return result;
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(
+ const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/,
+ nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+ const nn::OptionalTimeoutDuration& /*timeoutDurationAfterFence*/) const {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "IPreparedModel::executeFenced is not supported on 1.2 HAL service";
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+ sp<V1_0::IPreparedModel> resource = kPreparedModel;
+ return resource;
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Service.cpp b/neuralnetworks/1.2/utils/src/Service.cpp
new file mode 100644
index 0000000..110188f
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+ hal::utils::ResilientDevice::Factory makeDevice =
+ [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+ auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+ if (service == nullptr) {
+ return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+ }
+ return Device::create(name, std::move(service));
+ };
+
+ return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils