blob: 06e18d7d2cd3fadb46ae58bc3b58ca809a1fb7bc [file] [log] [blame]
/*
* Copyright (C) 2021 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 "ShimDevice"
#include "ShimDevice.h"
#include <NeuralNetworks.h>
#include <aidl/android/hardware/neuralnetworks/DataLocation.h>
#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
#include <aidl/android/hardware/neuralnetworks/Extension.h>
#include <aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.h>
#include <aidl/android/hardware/neuralnetworks/Memory.h>
#include <aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.h>
#include <aidl/android/hardware/neuralnetworks/OperandLifeTime.h>
#include <aidl/android/hardware/neuralnetworks/OperandPerformance.h>
#include <android-base/logging.h>
#include <android-base/scopeguard.h>
#include <android/binder_auto_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/hal/aidl/Conversions.h>
#include <algorithm>
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "ShimConverter.h"
#include "ShimPreparedModel.h"
#include "ShimUtils.h"
#include "SupportLibrary.h"
using namespace ::android::nn::sl_wrapper;
namespace aidl::android::hardware::neuralnetworks {
namespace {
constexpr std::optional<::android::nn::wrapper::ExecutePriority> convertToNDKPriority(
Priority priority) {
switch (priority) {
case Priority::LOW:
return ::android::nn::wrapper::ExecutePriority::LOW;
case Priority::MEDIUM:
return ::android::nn::wrapper::ExecutePriority::MEDIUM;
case Priority::HIGH:
return ::android::nn::wrapper::ExecutePriority::HIGH;
}
LOG(ERROR) << "unrecognized priority: " << static_cast<int32_t>(priority);
return std::nullopt;
}
constexpr std::optional<::android::nn::wrapper::ExecutePreference> convertToNDKPreference(
ExecutionPreference preference) {
switch (preference) {
case ExecutionPreference::LOW_POWER:
return ::android::nn::wrapper::ExecutePreference::PREFER_LOW_POWER;
case ExecutionPreference::FAST_SINGLE_ANSWER:
return ::android::nn::wrapper::ExecutePreference::PREFER_FAST_SINGLE_ANSWER;
case ExecutionPreference::SUSTAINED_SPEED:
return ::android::nn::wrapper::ExecutePreference::PREFER_SUSTAINED_SPEED;
}
LOG(ERROR) << "unrecognized preference: " << static_cast<int32_t>(preference);
return std::nullopt;
}
// Safely downcast an IPreparedModel object to ShimPreparedModel.
// This function will return nullptr if the IPreparedModel object is not originated from the
// shim process.
const ShimPreparedModel* castToShimPreparedModel(IPreparedModel* preparedModel) {
if (preparedModel->isRemote()) {
return nullptr;
}
// This static_cast is safe because ShimPreparedModel is the only class that implements
// the IPreparedModel interface in the sample driver process.
return static_cast<const ShimPreparedModel*>(preparedModel);
}
static PerformanceInfo convertPerformanceInfo(const SL_ANeuralNetworksPerformanceInfo& info) {
return {.execTime = info.execTime, .powerUsage = info.powerUsage};
}
Capabilities getCapabilities(const NnApiSupportLibrary* nnapi, ANeuralNetworksDevice* device) {
Capabilities capabilities;
SL_ANeuralNetworksPerformanceInfo performanceInfo;
nnapi->SL_ANeuralNetworksDevice_getPerformanceInfo(
device, SL_ANEURALNETWORKS_CAPABILITIES_PERFORMANCE_RELAXED_SCALAR, &performanceInfo);
capabilities.relaxedFloat32toFloat16PerformanceScalar = convertPerformanceInfo(performanceInfo);
nnapi->SL_ANeuralNetworksDevice_getPerformanceInfo(
device, SL_ANEURALNETWORKS_CAPABILITIES_PERFORMANCE_RELAXED_TENSOR, &performanceInfo);
capabilities.relaxedFloat32toFloat16PerformanceTensor = convertPerformanceInfo(performanceInfo);
nnapi->SL_ANeuralNetworksDevice_getPerformanceInfo(
device, SL_ANEURALNETWORKS_CAPABILITIES_PERFORMANCE_IF, &performanceInfo);
capabilities.ifPerformance = convertPerformanceInfo(performanceInfo);
nnapi->SL_ANeuralNetworksDevice_getPerformanceInfo(
device, SL_ANEURALNETWORKS_CAPABILITIES_PERFORMANCE_WHILE, &performanceInfo);
capabilities.whilePerformance = convertPerformanceInfo(performanceInfo);
constexpr auto fn = [](SL_ANeuralNetworksOperandPerformanceInfo info, void* context) {
auto* out = static_cast<std::vector<OperandPerformance>*>(context);
out->push_back(OperandPerformance{
.type = static_cast<OperandType>(info.operandType),
.info = convertPerformanceInfo(info.performanceInfo),
});
};
nnapi->SL_ANeuralNetworksDevice_forEachOperandPerformanceInfo(
device, static_cast<void*>(&capabilities.operandPerformance), fn);
return capabilities;
}
NumberOfCacheFiles getNumberOfCacheFilesNeeded(const NnApiSupportLibrary* nnapi,
ANeuralNetworksDevice* device) {
uint32_t numModelCacheFiles;
uint32_t numDataCacheFiles;
nnapi->SL_ANeuralNetworksDevice_getNumberOfCacheFilesNeeded(device, &numModelCacheFiles,
&numDataCacheFiles);
return {
.numModelCache = static_cast<int32_t>(numModelCacheFiles),
.numDataCache = static_cast<int32_t>(numDataCacheFiles),
};
}
std::vector<Extension> getVendorExtensions(const NnApiSupportLibrary* nnapi,
ANeuralNetworksDevice* device) {
uint32_t vendorExtensionCount;
nnapi->SL_ANeuralNetworksDevice_getVendorExtensionCount(device, &vendorExtensionCount);
std::vector<Extension> extensions(vendorExtensionCount);
for (uint32_t vendorExtensionIndex = 0; vendorExtensionIndex < vendorExtensionCount;
++vendorExtensionIndex) {
auto& extension = extensions[vendorExtensionIndex];
const char* extensionName;
nnapi->SL_ANeuralNetworksDevice_getVendorExtensionName(device, vendorExtensionIndex,
&extensionName);
extension.name = extensionName;
constexpr auto fn = [](SL_ANeuralNetworksExtensionOperandTypeInformation info,
void* context) {
auto* out = static_cast<std::vector<ExtensionOperandTypeInformation>*>(context);
out->push_back(ExtensionOperandTypeInformation{
.type = info.type,
.isTensor = info.isTensor,
.byteSize = static_cast<int32_t>(info.byteSize),
});
};
nnapi->SL_ANeuralNetworksDevice_forEachVendorExtensionOperandTypeInformation(
device, vendorExtensionIndex, static_cast<void*>(&extension.operandTypes), fn);
}
return extensions;
}
} // namespace
ShimDevice::ShimDevice(std::shared_ptr<const NnApiSupportLibrary> nnapi,
ANeuralNetworksDevice* device, std::string serviceName)
: mNnapi(std::move(nnapi)),
mBufferTracker(ShimBufferTracker::create()),
mServiceName(std::move(serviceName)),
mDevice(device),
mCapabilities(neuralnetworks::getCapabilities(mNnapi.get(), mDevice)),
mNumberOfCacheFiles(neuralnetworks::getNumberOfCacheFilesNeeded(mNnapi.get(), mDevice)),
mExtensions(neuralnetworks::getVendorExtensions(mNnapi.get(), mDevice)) {}
// Manages the data buffer for an operand.
class ShimBuffer : public BnBuffer {
public:
ShimBuffer(const NnApiSupportLibrary* nnApi, const ::android::nn::Dimensions initialDimensions,
const ::android::nn::OperandType type,
std::shared_ptr<::android::nn::sl_wrapper::Memory> memory,
std::unique_ptr<ShimBufferTracker::Token> token)
: kInitialDimensions(initialDimensions),
kType(type),
mNnApi(nnApi),
mMemory(std::move(memory)),
kToken(std::move(token)) {}
bool tensorHasUnspecifiedDimensions(::android::nn::OperandType type,
const ::android::nn::Dimensions& dimensions) {
if (!::android::nn::isExtension(type)) {
if (isNonExtensionScalar(type)) {
return false;
}
}
return dimensions.size() == 0 || std::any_of(dimensions.begin(), dimensions.end(),
[](int32_t dim) { return dim == 0; });
}
bool validateDimensions(const ::android::nn::Dimensions& dimensions) {
if (isNonExtensionScalar(kType)) {
if (!dimensions.empty()) {
LOG(ERROR) << "ShimBuffer::validateDimensions -- invalid dimensions for scalar "
"operand";
return false;
}
return true;
}
if (dimensions.empty()) {
if (tensorHasUnspecifiedDimensions(kType, kInitialDimensions)) {
LOG(ERROR) << "ShimBuffer::validateDimensions -- the initial dimensions are not "
"fully specified and no dimension update is provided: ";
return false;
}
} else {
if (tensorHasUnspecifiedDimensions(kType, dimensions)) {
LOG(ERROR) << "ShimBuffer::validateDimensions -- the updated dimensions are not "
"fully specified: ";
return false;
}
}
const auto combined = ::android::nn::combineDimensions(kInitialDimensions, dimensions);
if (!combined.has_value()) {
LOG(ERROR) << "ShimBuffer::validateDimensions -- incompatible dimensions";
return false;
}
return true;
}
ndk::ScopedAStatus copyFrom(const aidl::android::hardware::neuralnetworks::Memory& src,
const std::vector<int32_t>& dimensions) override {
auto memory = convertFromHAL(mNnApi, src);
if (!memory) {
LOG(ERROR) << "Failed to convert HAL Memory to SL memory";
return toAStatus(ErrorStatus::INVALID_ARGUMENT);
}
const auto unsignedDimensions = ::android::nn::toUnsigned(dimensions);
if (!unsignedDimensions.has_value()) {
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
unsignedDimensions.error().message);
}
if (!validateDimensions(unsignedDimensions.value())) {
LOG(ERROR) << "Invalid dimensions";
return toAStatus(ErrorStatus::INVALID_ARGUMENT);
}
Result result = memory->copyTo(*mMemory.get());
// Special case expected error status for uninitialized source memory
if (result == Result::BAD_DATA) {
// NNAPI Runtime reports both uninitialized memory
// and incompatible dimensions as BAD_DATA, but
// VTS expects to see INVALID_ARGUMENT for bad dimensions,
// and GENERAL_FAILURE for uninitialized memory.
if (memory->getSize() != mMemory->getSize()) {
return toAStatus(ErrorStatus::INVALID_ARGUMENT, "Incompatible sizes");
}
return toAStatus(ErrorStatus::GENERAL_FAILURE);
}
SLW2SAS_RETURN_IF_ERROR(result);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus copyTo(const Memory& dst) override {
auto memory = convertFromHAL(mNnApi, dst);
if (!memory) {
LOG(ERROR) << "Failed to convert HAL Memory to SL memory";
return toAStatus(ErrorStatus::INVALID_ARGUMENT);
}
Result result = mMemory->copyTo(*memory);
// Special case expected error status for uninitialized source memory
if (result == Result::BAD_DATA) {
// NNAPI Runtime reports both uninitialized memory
// and incompatible dimensions as BAD_DATA, but
// VTS expects to see INVALID_ARGUMENT for bad dimensions,
// and GENERAL_FAILURE for uninitialized memory.
if (memory->getSize() != mMemory->getSize()) {
return toAStatus(ErrorStatus::INVALID_ARGUMENT, "Incompatible sizes");
}
return toAStatus(ErrorStatus::GENERAL_FAILURE);
}
SLW2SAS_RETURN_IF_ERROR(result);
return ndk::ScopedAStatus::ok();
}
private:
const ::android::nn::Dimensions kInitialDimensions;
const ::android::nn::OperandType kType;
const NnApiSupportLibrary* mNnApi;
std::shared_ptr<::android::nn::sl_wrapper::Memory> mMemory;
const std::unique_ptr<ShimBufferTracker::Token> kToken;
};
::ndk::ScopedAStatus ShimDevice::allocate(const BufferDesc& desc,
const std::vector<IPreparedModelParcel>& preparedModels,
const std::vector<BufferRole>& inputRoles,
const std::vector<BufferRole>& outputRoles,
DeviceBuffer* buffer) {
if (!isValidDimension(desc.dimensions)) {
LOG(ERROR) << "ShimDriver::allocate -- passed invalid dimension values.";
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- passed invalid dimension values");
}
ANeuralNetworksMemoryDesc* slDesc = nullptr;
mNnapi->ANeuralNetworksMemoryDesc_create(&slDesc);
const auto slDescGuard = ::android::base::make_scope_guard(
[this, slDesc] { mNnapi->ANeuralNetworksMemoryDesc_free(slDesc); });
auto unsignedDimensions = ::android::nn::toUnsigned(desc.dimensions).value();
if (mNnapi->ANeuralNetworksMemoryDesc_setDimensions(slDesc, desc.dimensions.size(),
unsignedDimensions.data()) !=
ANEURALNETWORKS_NO_ERROR) {
LOG(ERROR) << "ShimDriver::allocate -- ANeuralNetworksMemoryDesc_setDimensions fail.";
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- ANeuralNetworksMemoryDesc_setDimensions fail");
}
constexpr auto getCompilation = [](IPreparedModel* preparedModel) -> const ShimPreparedModel* {
const auto* samplePreparedModel = castToShimPreparedModel(preparedModel);
if (samplePreparedModel == nullptr) {
LOG(ERROR) << "ShimDriver::allocate -- unknown remote IPreparedModel.";
return nullptr;
}
return samplePreparedModel;
};
std::optional<::android::nn::OperandType> type;
std::vector<uint32_t> dimensions = ::android::nn::toUnsigned(desc.dimensions).value();
for (const auto& role : inputRoles) {
if (role.modelIndex < 0 || role.modelIndex >= preparedModels.size()) {
LOG(ERROR) << "Invalid modelIndex value " << role.modelIndex;
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- Input role modeIndex with invalid value");
}
auto preparedModel = preparedModels[role.modelIndex];
if (preparedModel.preparedModel == nullptr) {
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- nullptr model");
}
auto pmodel = getCompilation(preparedModel.preparedModel.get());
if (pmodel == nullptr) {
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- nullptr model");
}
auto result = mNnapi->ANeuralNetworksMemoryDesc_addInputRole(
slDesc, pmodel->getCompilation().getHandle(), role.ioIndex, role.probability);
if (result != ANEURALNETWORKS_NO_ERROR) {
LOG(ERROR) << "SampleDriver::allocate -- ANeuralNetworksMemoryDesc_addInputRole fail.";
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- ANeuralNetworksMemoryDesc_addInputRole fail");
}
const auto& model = pmodel->getMainModel();
const auto& op = model.getOperands()[model.getInputs()[role.ioIndex]];
auto operandType = static_cast<::android::nn::OperandType>(op.operandType.type);
if (!type) {
type = operandType;
}
if (dimensions.empty()) {
dimensions = op.dimensions;
}
}
for (const auto& role : outputRoles) {
if (role.modelIndex < 0 || role.modelIndex >= preparedModels.size()) {
LOG(ERROR) << "Invalid modelIndex value " << role.modelIndex;
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- Ou0tput role modeIndex with invalid value");
}
auto preparedModel = preparedModels[role.modelIndex];
if (preparedModel.preparedModel == nullptr) {
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- nullptr model");
}
auto pmodel = getCompilation(preparedModel.preparedModel.get());
if (pmodel == nullptr) {
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- nullptr model");
}
auto result = mNnapi->ANeuralNetworksMemoryDesc_addOutputRole(
slDesc, pmodel->getCompilation().getHandle(), role.ioIndex, role.probability);
if (result != ANEURALNETWORKS_NO_ERROR) {
LOG(ERROR) << "SampleDriver::allocate -- ANeuralNetworksMemoryDesc_addInputRole fail.";
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- ANeuralNetworksMemoryDesc_addInputRole fail");
}
const auto& model = pmodel->getMainModel();
const auto& op = model.getOperands()[model.getInputs()[role.ioIndex]];
auto operandType = static_cast<::android::nn::OperandType>(op.operandType.type);
if (!type) {
type = operandType;
}
if (dimensions.empty()) {
dimensions = op.dimensions;
}
}
auto typeSize = ::android::nn::getNonExtensionSize(*type, dimensions);
if (!typeSize.has_value()) {
return toAStatus(ErrorStatus::INVALID_ARGUMENT,
"ShimDriver::allocate -- failed to get underlying type size, "
"possibly an extension type");
}
mNnapi->ANeuralNetworksMemoryDesc_finish(slDesc);
auto memory =
std::make_shared<::android::nn::sl_wrapper::Memory>(mNnapi.get(), slDesc, *typeSize);
if (!memory->isValid()) {
LOG(ERROR) << "ShimDriver::allocate -- ANeuralNetworksMemory_createFromDesc failed.";
return toAStatus(ErrorStatus::GENERAL_FAILURE,
"ShimDriver::allocate -- ANeuralNetworksMemory_createFromDesc failed");
}
auto token = mBufferTracker->add(memory);
if (token == nullptr) {
LOG(ERROR) << "ShimDriver::allocate -- ShimBufferTracker returned invalid token.";
return toAStatus(ErrorStatus::GENERAL_FAILURE,
"ShimDriver::allocate -- ShimBufferTracker returned invalid token.");
}
const uint32_t tokenValue = token->get();
auto shimbuffer = ndk::SharedRefBase::make<ShimBuffer>(mNnapi.get(), dimensions, *type,
std::move(memory), std::move(token));
buffer->buffer = std::move(shimbuffer);
buffer->token = tokenValue;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ShimDevice::getCapabilities(Capabilities* capabilities) {
*capabilities = mCapabilities;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ShimDevice::getNumberOfCacheFilesNeeded(NumberOfCacheFiles* numberOfCacheFiles) {
*numberOfCacheFiles = mNumberOfCacheFiles;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ShimDevice::getSupportedExtensions(std::vector<Extension>* extensions) {
*extensions = mExtensions;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ShimDevice::getSupportedOperations(const Model& model,
std::vector<bool>* supportedOperations) {
const auto numOperations = model.main.operations.size();
supportedOperations->resize(numOperations);
ErrorStatus convertErrorStatus = ErrorStatus::NONE;
std::vector<uint8_t> copiedOperandValues;
auto modelAndMemory =
convertFromHAL(mNnapi.get(), model, &copiedOperandValues, &convertErrorStatus);
if (!modelAndMemory || modelAndMemory->models.empty()) {
LOG(ERROR) << "Failed to convert HAL model to SL model";
return toAStatus(convertErrorStatus);
}
auto annModel = modelAndMemory->models[0].getHandle();
auto supportedOps = std::make_unique<bool[]>(numOperations);
auto result = mNnapi->ANeuralNetworksModel_getSupportedOperationsForDevices(
annModel, &mDevice, /*numDevices=*/1, supportedOps.get());
SLW2SAS_RETURN_IF_ERROR(result);
std::copy(supportedOps.get(), supportedOps.get() + numOperations, supportedOperations->begin());
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ShimDevice::getType(DeviceType* type) {
int32_t deviceType;
auto result = mNnapi->ANeuralNetworksDevice_getType(mDevice, &deviceType);
SLW2SAS_RETURN_IF_ERROR(result);
*type = static_cast<DeviceType>(deviceType);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ShimDevice::getVersionString(std::string* versionString) {
const char* buffer;
auto result = mNnapi->ANeuralNetworksDevice_getVersion(mDevice, &buffer);
SLW2SAS_RETURN_IF_ERROR(result);
*versionString = std::string(buffer);
return ndk::ScopedAStatus::ok();
}
static std::vector<int> getIntFds(const std::vector<::ndk::ScopedFileDescriptor>& scopedFds) {
std::vector<int> fds;
fds.reserve(scopedFds.size());
for (const auto& scopedFd : scopedFds) {
fds.push_back(scopedFd.get());
}
return fds;
}
ndk::ScopedAStatus ShimDevice::prepareModel(
const Model& model, ExecutionPreference preference, Priority priority, int64_t deadlineNs,
const std::vector<::ndk::ScopedFileDescriptor>& modelCache,
const std::vector<::ndk::ScopedFileDescriptor>& dataCache,
const std::vector<uint8_t>& token,
const std::shared_ptr<IPreparedModelCallback>& callback) {
// TODO(183398748): Run model preparation in detached thread.
if (callback == nullptr) {
return toAStatus(ErrorStatus::INVALID_ARGUMENT);
}
auto ndkPreference = convertToNDKPreference(preference);
if (!ndkPreference) {
callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
return toAStatus(ErrorStatus::INVALID_ARGUMENT);
}
auto ndkPriority = convertToNDKPriority(priority);
if (!ndkPriority) {
callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
return toAStatus(ErrorStatus::INVALID_ARGUMENT);
}
ErrorStatus convertErrorStatus = ErrorStatus::NONE;
std::vector<uint8_t> copiedOperandValues;
auto modelAndMemory =
convertFromHAL(mNnapi.get(), model, &copiedOperandValues, &convertErrorStatus);
if (!modelAndMemory || modelAndMemory->models.empty()) {
callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
return toAStatus(convertErrorStatus);
}
// b/185976051, past this point we pretend that compilation is asynchronous, and in
/// case of error we return OK status, but communicate the error through the callback.
auto compilation = ::android::nn::sl_wrapper::Compilation::createForDevice(
mNnapi.get(), &modelAndMemory->models[0], mDevice);
SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(compilation.first, callback);
SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(compilation.second.setPreference(*ndkPreference),
callback);
SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(compilation.second.setPriority(*ndkPriority),
callback);
if (deadlineNs > -1) {
std::chrono::time_point<::android::base::boot_clock> deadlinePoint(
std::chrono::nanoseconds{deadlineNs});
const auto currentTime = ::android::base::boot_clock::now();
const auto timeoutDuration = std::chrono::nanoseconds(deadlinePoint - currentTime);
if (timeoutDuration <= std::chrono::nanoseconds::zero()) {
callback->notify(ErrorStatus::MISSED_DEADLINE_TRANSIENT, nullptr);
return ndk::ScopedAStatus::ok();
}
SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(
compilation.second.setTimeout(std::max<uint64_t>(1, timeoutDuration.count())),
callback);
}
if (!modelCache.empty() || !dataCache.empty()) {
SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(
compilation.second.setCachingFromFds(getIntFds(modelCache), getIntFds(dataCache),
token),
callback);
}
SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(compilation.second.finish(), callback);
const std::shared_ptr<ShimPreparedModel> preparedModel =
ndk::SharedRefBase::make<ShimPreparedModel>(
mNnapi, mBufferTracker, std::move(compilation.second),
std::move(modelAndMemory->models), std::move(modelAndMemory->memory),
std::move(copiedOperandValues));
callback->notify(ErrorStatus::NONE, preparedModel);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ShimDevice::prepareModelFromCache(
int64_t /*deadlineNs*/, const std::vector<::ndk::ScopedFileDescriptor>& /*modelCache*/,
const std::vector<::ndk::ScopedFileDescriptor>& /*dataCache*/,
const std::vector<uint8_t>& /*token*/,
const std::shared_ptr<IPreparedModelCallback>& callback) {
// The NNAPI runtime will attempt to call this before falling back to
// ShimDevice::prepareModel(). This is not a LOG(ERROR) to avoid producing
// misleading logcat messages on every compilation request because there is
// technically nothing wrong.
LOG(DEBUG) << "ShimDevice::prepareModelFromCache() is not supported. Use "
"ShimDevice::prepareModel() instead.";
const auto ret = callback->notify(ErrorStatus::GENERAL_FAILURE, nullptr);
return toAStatus(ErrorStatus::GENERAL_FAILURE);
}
} // namespace aidl::android::hardware::neuralnetworks