From 534556391ae153b1e8fdd11b233e2dc6ecd1e530 Mon Sep 17 00:00:00 2001 From: Michael Butler Date: Mon, 25 Oct 2021 11:20:33 -0700 Subject: Create NN AIDL adapter This change adds the following adapters: * nn::IDevice -> BnDevice * nn::IPreparedModel -> BnPreparedModel * nn::IBurst -> BnBurst * nn::IBuffer -> BnBuffer Bug: N/A Test: mma Test: locally created a binderized service with this adapter code, which passed VtsHalNeuralnetworksTargetTest Change-Id: I966f65a1e4d75284c050b77f3f40c515e4970130 --- .../utils/adapter/hidl/src/PreparedModel.cpp | 436 +++++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp (limited to 'neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp') diff --git a/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp b/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp new file mode 100644 index 0000000000..a14e782b9b --- /dev/null +++ b/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp @@ -0,0 +1,436 @@ +/* + * 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 "Burst.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface +// lifetimes across processes and for protecting asynchronous calls across HIDL. + +namespace android::hardware::neuralnetworks::adapter { +namespace { + +template +auto convertInput(const Type& object) -> decltype(nn::convert(std::declval())) { + auto result = nn::convert(object); + if (!result.has_value()) { + result.error().code = nn::ErrorStatus::INVALID_ARGUMENT; + } + return result; +} + +nn::GeneralResult validateRequestForModel(const nn::Request& request, + const nn::Model& model) { + nn::GeneralResult version = nn::validateRequestForModel(request, model); + if (!version.ok()) { + version.error().code = nn::ErrorStatus::INVALID_ARGUMENT; + } + return version; +} + +class FencedExecutionCallback final : public V1_3::IFencedExecutionCallback { + public: + explicit FencedExecutionCallback(const nn::ExecuteFencedInfoCallback& callback) + : kCallback(callback) { + CHECK(callback != nullptr); + } + + Return getExecutionInfo(getExecutionInfo_cb cb) override { + const auto result = kCallback(); + if (!result.has_value()) { + const auto& [message, code] = result.error(); + const auto status = + V1_3::utils::convert(code).value_or(V1_3::ErrorStatus::GENERAL_FAILURE); + LOG(ERROR) << message; + cb(status, V1_2::utils::kNoTiming, V1_2::utils::kNoTiming); + return Void(); + } + const auto [timingLaunched, timingFenced] = result.value(); + const auto hidlTimingLaunched = V1_3::utils::convert(timingLaunched).value(); + const auto hidlTimingFenced = V1_3::utils::convert(timingFenced).value(); + cb(V1_3::ErrorStatus::NONE, hidlTimingLaunched, hidlTimingFenced); + return Void(); + } + + private: + const nn::ExecuteFencedInfoCallback kCallback; +}; + +using ExecutionResult = nn::ExecutionResult, nn::Timing>>; + +void notify(V1_0::IExecutionCallback* callback, nn::ErrorStatus status, + const std::vector& /*outputShapes*/, const nn::Timing& /*timing*/) { + if (callback != nullptr) { + const auto hidlStatus = V1_0::utils::convert(status).value(); + const auto ret = callback->notify(hidlStatus); + if (!ret.isOk()) { + LOG(ERROR) << "V1_0::IExecutionCallback::notify failed with " << ret.description(); + } + } +} + +void notify(V1_2::IExecutionCallback* callback, nn::ErrorStatus status, + const std::vector& outputShapes, const nn::Timing& timing) { + if (callback != nullptr) { + const auto hidlStatus = V1_2::utils::convert(status).value(); + const auto hidlOutputShapes = V1_2::utils::convert(outputShapes).value(); + const auto hidlTiming = V1_2::utils::convert(timing).value(); + const auto ret = callback->notify_1_2(hidlStatus, hidlOutputShapes, hidlTiming); + if (!ret.isOk()) { + LOG(ERROR) << "V1_2::IExecutionCallback::notify_1_2 failed with " << ret.description(); + } + } +} + +void notify(V1_3::IExecutionCallback* callback, nn::ErrorStatus status, + const std::vector& outputShapes, const nn::Timing& timing) { + if (callback != nullptr) { + const auto hidlStatus = V1_3::utils::convert(status).value(); + const auto hidlOutputShapes = V1_3::utils::convert(outputShapes).value(); + const auto hidlTiming = V1_3::utils::convert(timing).value(); + const auto ret = callback->notify_1_3(hidlStatus, hidlOutputShapes, hidlTiming); + if (!ret.isOk()) { + LOG(ERROR) << "V1_3::IExecutionCallback::notify_1_3 failed with " << ret.description(); + } + } +} + +template +void notify(CallbackType* callback, ExecutionResult result) { + if (!result.has_value()) { + const auto [message, status, outputShapes] = std::move(result).error(); + LOG(ERROR) << message; + notify(callback, status, outputShapes, {}); + } else { + const auto [outputShapes, timing] = std::move(result).value(); + notify(callback, nn::ErrorStatus::NONE, outputShapes, timing); + } +} + +nn::GeneralResult execute(const nn::SharedPreparedModel& preparedModel, uid_t userId, + const Executor& executor, const V1_0::Request& request, + const sp& callback) { + if (callback.get() == nullptr) { + return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback"; + } + + auto nnRequest = NN_TRY(convertInput(request)); + + const std::any resource = preparedModel->getUnderlyingResource(); + if (const auto* model = std::any_cast(&resource)) { + CHECK(*model != nullptr); + NN_TRY(adapter::validateRequestForModel(nnRequest, **model)); + } + + Task task = [preparedModel, nnRequest = std::move(nnRequest), callback] { + auto result = preparedModel->execute(nnRequest, nn::MeasureTiming::NO, {}, {}); + notify(callback.get(), std::move(result)); + }; + executor(std::move(task), userId, {}); + + return {}; +} + +nn::GeneralResult execute_1_2(const nn::SharedPreparedModel& preparedModel, uid_t userId, + const Executor& executor, const V1_0::Request& request, + V1_2::MeasureTiming measure, + const sp& callback) { + if (callback.get() == nullptr) { + return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback"; + } + + auto nnRequest = NN_TRY(convertInput(request)); + const auto nnMeasure = NN_TRY(convertInput(measure)); + + const std::any resource = preparedModel->getUnderlyingResource(); + if (const auto* model = std::any_cast(&resource)) { + CHECK(*model != nullptr); + NN_TRY(adapter::validateRequestForModel(nnRequest, **model)); + } + + Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, callback] { + auto result = preparedModel->execute(nnRequest, nnMeasure, {}, {}); + notify(callback.get(), std::move(result)); + }; + executor(std::move(task), userId, {}); + + return {}; +} + +nn::GeneralResult execute_1_3(const nn::SharedPreparedModel& preparedModel, uid_t userId, + const Executor& executor, const V1_3::Request& request, + V1_2::MeasureTiming measure, + const V1_3::OptionalTimePoint& deadline, + const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, + const sp& callback) { + if (callback.get() == nullptr) { + return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback"; + } + + auto nnRequest = NN_TRY(convertInput(request)); + const auto nnMeasure = NN_TRY(convertInput(measure)); + const auto nnDeadline = NN_TRY(convertInput(deadline)); + const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration)); + + const std::any resource = preparedModel->getUnderlyingResource(); + if (const auto* model = std::any_cast(&resource)) { + CHECK(*model != nullptr); + NN_TRY(adapter::validateRequestForModel(nnRequest, **model)); + } + + Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, nnDeadline, + nnLoopTimeoutDuration, callback] { + auto result = + preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration); + notify(callback.get(), std::move(result)); + }; + executor(std::move(task), userId, nnDeadline); + + return {}; +} + +nn::ExecutionResult, V1_2::Timing>> executeSynchronously( + const nn::SharedPreparedModel& preparedModel, const V1_0::Request& request, + V1_2::MeasureTiming measure) { + const auto nnRequest = NN_TRY(convertInput(request)); + const auto nnMeasure = NN_TRY(convertInput(measure)); + + const auto [outputShapes, timing] = + NN_TRY(preparedModel->execute(nnRequest, nnMeasure, {}, {})); + + auto hidlOutputShapes = NN_TRY(V1_2::utils::convert(outputShapes)); + const auto hidlTiming = NN_TRY(V1_2::utils::convert(timing)); + return std::make_pair(std::move(hidlOutputShapes), hidlTiming); +} + +nn::ExecutionResult, V1_2::Timing>> executeSynchronously_1_3( + const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request, + V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline, + const V1_3::OptionalTimeoutDuration& loopTimeoutDuration) { + const auto nnRequest = NN_TRY(convertInput(request)); + const auto nnMeasure = NN_TRY(convertInput(measure)); + const auto nnDeadline = NN_TRY(convertInput(deadline)); + const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration)); + + const auto [outputShapes, timing] = + NN_TRY(preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration)); + + auto hidlOutputShapes = NN_TRY(V1_3::utils::convert(outputShapes)); + const auto hidlTiming = NN_TRY(V1_3::utils::convert(timing)); + return std::make_pair(std::move(hidlOutputShapes), hidlTiming); +} + +nn::GeneralResult> convertSyncFences( + const hidl_vec& handles) { + auto nnHandles = NN_TRY(convertInput(handles)); + std::vector syncFences; + syncFences.reserve(handles.size()); + for (auto&& handle : nnHandles) { + if (auto syncFence = nn::SyncFence::create(std::move(handle)); !syncFence.ok()) { + return nn::error(nn::ErrorStatus::INVALID_ARGUMENT) << std::move(syncFence).error(); + } else { + syncFences.push_back(std::move(syncFence).value()); + } + } + return syncFences; +} + +nn::GeneralResult> configureExecutionBurst( + const nn::SharedPreparedModel& preparedModel, const sp& callback, + const MQDescriptorSync& requestChannel, + const MQDescriptorSync& resultChannel) { + auto burstExecutor = NN_TRY(preparedModel->configureExecutionBurst()); + return Burst::create(callback, requestChannel, resultChannel, std::move(burstExecutor), + V1_2::utils::getBurstServerPollingTimeWindow()); +} + +nn::GeneralResult>> executeFenced( + const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request, + const hidl_vec& waitFor, V1_2::MeasureTiming measure, + const V1_3::OptionalTimePoint& deadline, + const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, + const V1_3::OptionalTimeoutDuration& duration) { + const auto nnRequest = NN_TRY(convertInput(request)); + const auto nnWaitFor = NN_TRY(convertSyncFences(waitFor)); + const auto nnMeasure = NN_TRY(convertInput(measure)); + const auto nnDeadline = NN_TRY(convertInput(deadline)); + const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration)); + const auto nnDuration = NN_TRY(convertInput(duration)); + + auto [syncFence, executeFencedCallback] = NN_TRY(preparedModel->executeFenced( + nnRequest, nnWaitFor, nnMeasure, nnDeadline, nnLoopTimeoutDuration, nnDuration)); + + auto hidlSyncFence = NN_TRY(V1_3::utils::convert(syncFence.getSharedHandle())); + auto hidlExecuteFencedCallback = sp::make(executeFencedCallback); + return std::make_pair(std::move(hidlSyncFence), std::move(hidlExecuteFencedCallback)); +} + +} // namespace + +PreparedModel::PreparedModel(nn::SharedPreparedModel preparedModel, Executor executor, uid_t userId) + : kPreparedModel(std::move(preparedModel)), kExecutor(std::move(executor)), kUserId(userId) { + CHECK(kPreparedModel != nullptr); + CHECK(kExecutor != nullptr); +} + +nn::SharedPreparedModel PreparedModel::getUnderlyingPreparedModel() const { + return kPreparedModel; +} + +Return PreparedModel::execute(const V1_0::Request& request, + const sp& callback) { + auto result = adapter::execute(kPreparedModel, kUserId, kExecutor, request, callback); + if (!result.has_value()) { + auto [message, code] = std::move(result).error(); + LOG(ERROR) << "adapter::PreparedModel::execute failed with " << code << ": " << message; + notify(callback.get(), code, {}, {}); + return V1_0::utils::convert(code).value(); + } + return V1_0::ErrorStatus::NONE; +} + +Return PreparedModel::execute_1_2(const V1_0::Request& request, + V1_2::MeasureTiming measure, + const sp& callback) { + auto result = + adapter::execute_1_2(kPreparedModel, kUserId, kExecutor, request, measure, callback); + if (!result.has_value()) { + auto [message, code] = std::move(result).error(); + LOG(ERROR) << "adapter::PreparedModel::execute_1_2 failed with " << code << ": " << message; + notify(callback.get(), code, {}, {}); + return V1_2::utils::convert(code).value(); + } + return V1_0::ErrorStatus::NONE; +} + +Return PreparedModel::execute_1_3( + const V1_3::Request& request, V1_2::MeasureTiming measure, + const V1_3::OptionalTimePoint& deadline, + const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, + const sp& callback) { + auto result = adapter::execute_1_3(kPreparedModel, kUserId, kExecutor, request, measure, + deadline, loopTimeoutDuration, callback); + if (!result.has_value()) { + auto [message, code] = std::move(result).error(); + LOG(ERROR) << "adapter::PreparedModel::execute_1_3 failed with " << code << ": " << message; + notify(callback.get(), code, {}, {}); + return V1_3::utils::convert(code).value(); + } + return V1_3::ErrorStatus::NONE; +} + +Return PreparedModel::executeSynchronously(const V1_0::Request& request, + V1_2::MeasureTiming measure, + executeSynchronously_cb cb) { + auto result = adapter::executeSynchronously(kPreparedModel, request, measure); + if (!result.has_value()) { + auto [message, code, outputShapes] = std::move(result).error(); + LOG(ERROR) << "adapter::PreparedModel::executeSynchronously failed with " << code << ": " + << message; + cb(V1_2::utils::convert(code).value(), V1_2::utils::convert(outputShapes).value(), + V1_2::utils::kNoTiming); + return Void(); + } + auto [outputShapes, timing] = std::move(result).value(); + cb(V1_0::ErrorStatus::NONE, outputShapes, timing); + return Void(); +} + +Return PreparedModel::executeSynchronously_1_3( + const V1_3::Request& request, V1_2::MeasureTiming measure, + const V1_3::OptionalTimePoint& deadline, + const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, executeSynchronously_1_3_cb cb) { + auto result = adapter::executeSynchronously_1_3(kPreparedModel, request, measure, deadline, + loopTimeoutDuration); + if (!result.has_value()) { + auto [message, code, outputShapes] = std::move(result).error(); + LOG(ERROR) << "adapter::PreparedModel::executeSynchronously_1_3 failed with " << code + << ": " << message; + cb(V1_3::utils::convert(code).value(), V1_3::utils::convert(outputShapes).value(), + V1_2::utils::kNoTiming); + return Void(); + } + auto [outputShapes, timing] = std::move(result).value(); + cb(V1_3::ErrorStatus::NONE, outputShapes, timing); + return Void(); +} + +Return PreparedModel::configureExecutionBurst( + const sp& callback, + const MQDescriptorSync& requestChannel, + const MQDescriptorSync& resultChannel, + configureExecutionBurst_cb cb) { + auto result = adapter::configureExecutionBurst(kPreparedModel, callback, requestChannel, + resultChannel); + if (!result.has_value()) { + auto [message, code] = std::move(result).error(); + LOG(ERROR) << "adapter::PreparedModel::configureExecutionBurst failed with " << code << ": " + << message; + cb(V1_2::utils::convert(code).value(), nullptr); + return Void(); + } + auto burstContext = std::move(result).value(); + cb(V1_0::ErrorStatus::NONE, std::move(burstContext)); + return Void(); +} + +Return PreparedModel::executeFenced(const V1_3::Request& request, + const hidl_vec& waitFor, + V1_2::MeasureTiming measure, + const V1_3::OptionalTimePoint& deadline, + const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, + const V1_3::OptionalTimeoutDuration& duration, + executeFenced_cb callback) { + auto result = adapter::executeFenced(kPreparedModel, request, waitFor, measure, deadline, + loopTimeoutDuration, duration); + if (!result.has_value()) { + auto [message, code] = std::move(result).error(); + LOG(ERROR) << "adapter::PreparedModel::executeFenced failed with " << code << ": " + << message; + callback(V1_3::utils::convert(code).value(), {}, nullptr); + return Void(); + } + auto [syncFence, executeFencedCallback] = std::move(result).value(); + callback(V1_3::ErrorStatus::NONE, syncFence, executeFencedCallback); + return Void(); +} + +} // namespace android::hardware::neuralnetworks::adapter -- cgit v1.2.3 From 594cc78a7b5330210397c004d53e52db9148e297 Mon Sep 17 00:00:00 2001 From: Michael Butler Date: Tue, 11 Jan 2022 22:53:49 -0800 Subject: Remove uid from NN HIDL adapter Having the adapter retrieve the uid is redundant because the implementor is already able to get the uid directly themselves with IPCThreadState::self()->getCallingUid(). Bug: N/A Test: mma Change-Id: Ifeffea053cb92556be1aae8b17a94fafa1ac98e0 --- .../utils/adapter/hidl/src/PreparedModel.cpp | 27 ++++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp') diff --git a/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp b/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp index a14e782b9b..71060d5ca7 100644 --- a/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp +++ b/neuralnetworks/utils/adapter/hidl/src/PreparedModel.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -37,7 +36,6 @@ #include #include #include -#include #include #include @@ -145,7 +143,7 @@ void notify(CallbackType* callback, ExecutionResult result) { } } -nn::GeneralResult execute(const nn::SharedPreparedModel& preparedModel, uid_t userId, +nn::GeneralResult execute(const nn::SharedPreparedModel& preparedModel, const Executor& executor, const V1_0::Request& request, const sp& callback) { if (callback.get() == nullptr) { @@ -164,12 +162,12 @@ nn::GeneralResult execute(const nn::SharedPreparedModel& preparedModel, ui auto result = preparedModel->execute(nnRequest, nn::MeasureTiming::NO, {}, {}); notify(callback.get(), std::move(result)); }; - executor(std::move(task), userId, {}); + executor(std::move(task), {}); return {}; } -nn::GeneralResult execute_1_2(const nn::SharedPreparedModel& preparedModel, uid_t userId, +nn::GeneralResult execute_1_2(const nn::SharedPreparedModel& preparedModel, const Executor& executor, const V1_0::Request& request, V1_2::MeasureTiming measure, const sp& callback) { @@ -190,12 +188,12 @@ nn::GeneralResult execute_1_2(const nn::SharedPreparedModel& preparedModel auto result = preparedModel->execute(nnRequest, nnMeasure, {}, {}); notify(callback.get(), std::move(result)); }; - executor(std::move(task), userId, {}); + executor(std::move(task), {}); return {}; } -nn::GeneralResult execute_1_3(const nn::SharedPreparedModel& preparedModel, uid_t userId, +nn::GeneralResult execute_1_3(const nn::SharedPreparedModel& preparedModel, const Executor& executor, const V1_3::Request& request, V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline, @@ -222,7 +220,7 @@ nn::GeneralResult execute_1_3(const nn::SharedPreparedModel& preparedModel preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration); notify(callback.get(), std::move(result)); }; - executor(std::move(task), userId, nnDeadline); + executor(std::move(task), nnDeadline); return {}; } @@ -305,8 +303,8 @@ nn::GeneralResult>> ex } // namespace -PreparedModel::PreparedModel(nn::SharedPreparedModel preparedModel, Executor executor, uid_t userId) - : kPreparedModel(std::move(preparedModel)), kExecutor(std::move(executor)), kUserId(userId) { +PreparedModel::PreparedModel(nn::SharedPreparedModel preparedModel, Executor executor) + : kPreparedModel(std::move(preparedModel)), kExecutor(std::move(executor)) { CHECK(kPreparedModel != nullptr); CHECK(kExecutor != nullptr); } @@ -317,7 +315,7 @@ nn::SharedPreparedModel PreparedModel::getUnderlyingPreparedModel() const { Return PreparedModel::execute(const V1_0::Request& request, const sp& callback) { - auto result = adapter::execute(kPreparedModel, kUserId, kExecutor, request, callback); + auto result = adapter::execute(kPreparedModel, kExecutor, request, callback); if (!result.has_value()) { auto [message, code] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::execute failed with " << code << ": " << message; @@ -330,8 +328,7 @@ Return PreparedModel::execute(const V1_0::Request& request, Return PreparedModel::execute_1_2(const V1_0::Request& request, V1_2::MeasureTiming measure, const sp& callback) { - auto result = - adapter::execute_1_2(kPreparedModel, kUserId, kExecutor, request, measure, callback); + auto result = adapter::execute_1_2(kPreparedModel, kExecutor, request, measure, callback); if (!result.has_value()) { auto [message, code] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::execute_1_2 failed with " << code << ": " << message; @@ -346,8 +343,8 @@ Return PreparedModel::execute_1_3( const V1_3::OptionalTimePoint& deadline, const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, const sp& callback) { - auto result = adapter::execute_1_3(kPreparedModel, kUserId, kExecutor, request, measure, - deadline, loopTimeoutDuration, callback); + auto result = adapter::execute_1_3(kPreparedModel, kExecutor, request, measure, deadline, + loopTimeoutDuration, callback); if (!result.has_value()) { auto [message, code] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::execute_1_3 failed with " << code << ": " << message; -- cgit v1.2.3