summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Butler <butlermichael@google.com>2020-11-17 01:44:38 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-11-17 01:44:38 +0000
commit75ec7d8cc4e55c77e32090215c28f9ecc0d41057 (patch)
tree0b533b9ac4b356a60d063767ee70b50a98f08af9
parent638a57c1b06b4369487c6d36ae6c7bbc245ec14d (diff)
parentc9f7baa4652bafc75abac0f3ed2b388650cb2e32 (diff)
downloadml-75ec7d8cc4e55c77e32090215c28f9ecc0d41057.tar.gz
Merge "Introduce NNAPI Canonical Interface types"
-rw-r--r--nn/common/SharedMemory.cpp8
-rw-r--r--nn/common/SharedMemoryAndroid.cpp48
-rw-r--r--nn/common/SharedMemoryHost.cpp36
-rw-r--r--nn/common/TypeUtils.cpp14
-rw-r--r--nn/common/Types.cpp65
-rw-r--r--nn/common/include/nnapi/IBuffer.h78
-rw-r--r--nn/common/include/nnapi/IDevice.h367
-rw-r--r--nn/common/include/nnapi/IPreparedModel.h177
-rw-r--r--nn/common/include/nnapi/Result.h21
-rw-r--r--nn/common/include/nnapi/SharedMemory.h14
-rw-r--r--nn/common/include/nnapi/TypeUtils.h1
-rw-r--r--nn/common/include/nnapi/Types.h49
-rw-r--r--nn/common/include/nnapi/Validation.h5
13 files changed, 822 insertions, 61 deletions
diff --git a/nn/common/SharedMemory.cpp b/nn/common/SharedMemory.cpp
index 9186be20e..3c848c315 100644
--- a/nn/common/SharedMemory.cpp
+++ b/nn/common/SharedMemory.cpp
@@ -67,7 +67,7 @@ bool MutableMemoryBuilder::empty() const {
return mSize == 0;
}
-Result<Memory> MutableMemoryBuilder::finish() {
+GeneralResult<Memory> MutableMemoryBuilder::finish() {
return createSharedMemory(mSize);
}
@@ -84,7 +84,7 @@ bool ConstantMemoryBuilder::empty() const {
return mBuilder.empty();
}
-Result<Memory> ConstantMemoryBuilder::finish() {
+GeneralResult<Memory> ConstantMemoryBuilder::finish() {
// Allocate the memory.
auto memory = NN_TRY(mBuilder.finish());
@@ -92,10 +92,6 @@ Result<Memory> ConstantMemoryBuilder::finish() {
const auto [pointer, size, context] = NN_TRY(map(memory););
// Get mutable pointer.
- if (!std::holds_alternative<void*>(pointer)) {
- return NN_ERROR()
- << "MemoryBuilder::finish failed because the mapped pointer is not mutable";
- }
uint8_t* mutablePointer = static_cast<uint8_t*>(std::get<void*>(pointer));
// Copy data to the memory pool.
diff --git a/nn/common/SharedMemoryAndroid.cpp b/nn/common/SharedMemoryAndroid.cpp
index caa83a6e8..9baca73c5 100644
--- a/nn/common/SharedMemoryAndroid.cpp
+++ b/nn/common/SharedMemoryAndroid.cpp
@@ -46,7 +46,7 @@ using ::android::hidl::allocator::V1_0::IAllocator;
const char* const kAllocatorService = "ashmem";
-Result<hidl_memory> allocateSharedMemory(size_t size) {
+GeneralResult<hidl_memory> allocateSharedMemory(size_t size) {
static const auto allocator = IAllocator::getService(kAllocatorService);
CHECK_GT(size, 0u);
@@ -59,7 +59,8 @@ Result<hidl_memory> allocateSharedMemory(size_t size) {
allocator->allocate(size, fn);
if (!maybeMemory.valid()) {
- return NN_ERROR() << "IAllocator::allocate returned an invalid (empty) memory object";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+ << "IAllocator::allocate returned an invalid (empty) memory object";
}
return maybeMemory;
@@ -85,19 +86,19 @@ hidl_memory createHidlMemory(const Memory& memory) {
return copiedMemory;
}
-Result<Mapping> mapAshmem(const Memory& memory) {
+GeneralResult<Mapping> mapAshmem(const Memory& memory) {
const auto hidlMemory = createHidlMemory(memory);
const auto mapping = mapMemory(hidlMemory);
if (mapping == nullptr) {
- return NN_ERROR() << "Failed to map memory";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to map memory";
}
auto* const pointer = mapping->getPointer().withDefault(nullptr);
if (pointer == nullptr) {
- return NN_ERROR() << "Failed to get the mapped pointer";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to get the mapped pointer";
}
const auto fullSize = mapping->getSize().withDefault(0);
if (fullSize == 0 || fullSize > std::numeric_limits<size_t>::max()) {
- return NN_ERROR() << "Failed to get the mapped size";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to get the mapped size";
}
const size_t size = static_cast<size_t>(fullSize);
@@ -113,7 +114,7 @@ struct MmapFdMappingContext {
std::any context;
};
-Result<Mapping> mapMemFd(const Memory& memory) {
+GeneralResult<Mapping> mapMemFd(const Memory& memory) {
const size_t size = memory.size;
const native_handle_t* handle = memory.handle->handle();
const int fd = handle->data[0];
@@ -122,7 +123,7 @@ Result<Mapping> mapMemFd(const Memory& memory) {
std::shared_ptr<base::MappedFile> mapping = base::MappedFile::FromFd(fd, offset, size, prot);
if (mapping == nullptr) {
- return NN_ERROR() << "Can't mmap the file descriptor.";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Can't mmap the file descriptor.";
}
void* data = mapping->data();
@@ -130,7 +131,7 @@ Result<Mapping> mapMemFd(const Memory& memory) {
return Mapping{.pointer = data, .size = size, .context = std::move(context)};
}
-Result<Mapping> mapAhwbBlobMemory(const Memory& memory) {
+GeneralResult<Mapping> mapAhwbBlobMemory(const Memory& memory) {
const auto* handle = memory.handle->handle();
const auto size = memory.size;
const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
@@ -153,13 +154,15 @@ Result<Mapping> mapAhwbBlobMemory(const Memory& memory) {
status_t status = AHardwareBuffer_createFromHandle(
&desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &hardwareBuffer);
if (status != NO_ERROR) {
- return NN_ERROR() << "Can't create AHardwareBuffer from handle. Error: " << status;
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+ << "Can't create AHardwareBuffer from handle. Error: " << status;
}
void* data = nullptr;
status = AHardwareBuffer_lock(hardwareBuffer, usage, -1, nullptr, &data);
if (status != NO_ERROR) {
- return NN_ERROR() << "Can't lock the AHardwareBuffer. Error: " << status;
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+ << "Can't lock the AHardwareBuffer. Error: " << status;
// TODO(b/169166682): do we need to call AHardwareBuffer_release?
}
@@ -175,27 +178,28 @@ Result<Mapping> mapAhwbBlobMemory(const Memory& memory) {
return Mapping{.pointer = data, .size = size, .context = std::move(sharedScoped)};
}
-Result<Mapping> mapAhwbMemory(const Memory& /*memory*/) {
- return NN_ERROR() << "Unable to map non-BLOB AHardwareBuffer memory";
+GeneralResult<Mapping> mapAhwbMemory(const Memory& /*memory*/) {
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+ << "Unable to map non-BLOB AHardwareBuffer memory";
}
} // namespace
-Result<Memory> createSharedMemory(size_t size) {
+GeneralResult<Memory> createSharedMemory(size_t size) {
const auto memory = NN_TRY(allocateSharedMemory(size));
return createSharedMemoryFromHidlMemory(memory);
}
-Result<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
+GeneralResult<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
if (size == 0 || fd < 0) {
- return NN_ERROR() << "Invalid size or fd";
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Invalid size or fd";
}
// Duplicate the file descriptor so the resultant Memory owns its own version.
int dupfd = dup(fd);
if (dupfd == -1) {
// TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
- return NN_ERROR() << "Failed to dup the fd";
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Failed to dup the fd";
}
// Create a temporary native handle to own the dupfd.
@@ -203,7 +207,7 @@ Result<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t of
if (nativeHandle == nullptr) {
close(dupfd);
// TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
- return NN_ERROR() << "Failed to create native_handle";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to create native_handle";
}
const auto [lowOffsetBits, highOffsetBits] = getIntsFromOffset(offset);
@@ -219,11 +223,11 @@ Result<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t of
return Memory{.handle = std::move(ownedHandle), .size = size, .name = "mmap_fd"};
}
-Result<Memory> createSharedMemoryFromHidlMemory(const hardware::hidl_memory& memory) {
+GeneralResult<Memory> createSharedMemoryFromHidlMemory(const hardware::hidl_memory& memory) {
return createMemory(memory);
}
-Result<Memory> createSharedMemoryFromAHWB(const AHardwareBuffer& ahwb) {
+GeneralResult<Memory> createSharedMemoryFromAHWB(const AHardwareBuffer& ahwb) {
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(&ahwb, &bufferDesc);
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(&ahwb);
@@ -243,7 +247,7 @@ Result<Memory> createSharedMemoryFromAHWB(const AHardwareBuffer& ahwb) {
return Memory{.handle = std::move(nativeHandle), .size = 0, .name = "hardware_buffer"};
}
-Result<Mapping> map(const Memory& memory) {
+GeneralResult<Mapping> map(const Memory& memory) {
if (memory.name == "ashmem") {
return mapAshmem(memory);
}
@@ -256,7 +260,7 @@ Result<Mapping> map(const Memory& memory) {
if (memory.name == "hardware_buffer") {
return mapAhwbMemory(memory);
}
- return NN_ERROR() << "Cannot map unknown memory " << memory.name;
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map unknown memory " << memory.name;
}
bool flush(const Mapping& mapping) {
diff --git a/nn/common/SharedMemoryHost.cpp b/nn/common/SharedMemoryHost.cpp
index bc29d1ff1..231977cf7 100644
--- a/nn/common/SharedMemoryHost.cpp
+++ b/nn/common/SharedMemoryHost.cpp
@@ -32,7 +32,7 @@
namespace android::nn {
namespace {
-Result<Mapping> mapAshmem(const Memory& memory) {
+GeneralResult<Mapping> mapAshmem(const Memory& memory) {
CHECK_LE(memory.size, std::numeric_limits<uint32_t>::max());
const auto size = memory.size;
@@ -40,7 +40,7 @@ Result<Mapping> mapAshmem(const Memory& memory) {
std::shared_ptr<base::MappedFile> mapping =
base::MappedFile::FromFd(fd, /*offset=*/0, size, PROT_READ | PROT_WRITE);
if (mapping == nullptr) {
- return NN_ERROR() << "Can't mmap the file descriptor.";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Can't mmap the file descriptor.";
}
void* data = mapping->data();
@@ -52,7 +52,7 @@ struct MmapFdMappingContext {
std::any context;
};
-Result<Mapping> mapMemFd(const Memory& memory) {
+GeneralResult<Mapping> mapMemFd(const Memory& memory) {
const size_t size = memory.size;
const native_handle_t* handle = memory.handle->handle();
const int fd = handle->data[0];
@@ -61,7 +61,7 @@ Result<Mapping> mapMemFd(const Memory& memory) {
std::shared_ptr<base::MappedFile> mapping = base::MappedFile::FromFd(fd, offset, size, prot);
if (mapping == nullptr) {
- return NN_ERROR() << "Can't mmap the file descriptor.";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Can't mmap the file descriptor.";
}
void* data = mapping->data();
@@ -71,16 +71,17 @@ Result<Mapping> mapMemFd(const Memory& memory) {
} // namespace
-Result<Memory> createSharedMemory(size_t size) {
+GeneralResult<Memory> createSharedMemory(size_t size) {
int fd = ashmem_create_region("NnapiAshmem", size);
if (fd < 0) {
- return NN_ERROR() << "ashmem_create_region(" << size << ") fails with " << fd;
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+ << "ashmem_create_region(" << size << ") fails with " << fd;
}
native_handle_t* handle = native_handle_create(1, 0);
if (handle == nullptr) {
// TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
- return NN_ERROR() << "Failed to create native_handle";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to create native_handle";
}
handle->data[0] = fd;
@@ -91,16 +92,16 @@ Result<Memory> createSharedMemory(size_t size) {
return Memory{.handle = std::move(nativeHandle), .size = size, .name = "ashmem"};
}
-Result<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
+GeneralResult<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
if (size == 0 || fd < 0) {
- return NN_ERROR() << "Invalid size or fd";
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Invalid size or fd";
}
// Duplicate the file descriptor so the resultant Memory owns its own version.
int dupfd = dup(fd);
if (dupfd == -1) {
// TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
- return NN_ERROR() << "Failed to dup the fd";
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Failed to dup the fd";
}
// Create a temporary native handle to own the dupfd.
@@ -108,7 +109,7 @@ Result<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t of
if (nativeHandle == nullptr) {
close(dupfd);
// TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
- return NN_ERROR() << "Failed to create native_handle";
+ return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to create native_handle";
}
const auto [lowOffsetBits, highOffsetBits] = getIntsFromOffset(offset);
@@ -124,22 +125,23 @@ Result<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t of
return Memory{.handle = std::move(ownedHandle), .size = size, .name = "mmap_fd"};
}
-Result<Memory> createSharedMemoryFromHidlMemory(const hardware::hidl_memory& /*memory*/) {
- return NN_ERROR() << "hidl_memory not supported on host";
+GeneralResult<Memory> createSharedMemoryFromHidlMemory(const hardware::hidl_memory& /*memory*/) {
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "hidl_memory not supported on host";
}
-Result<Memory> createSharedMemoryFromAHWB(const AHardwareBuffer& /*ahwb*/) {
- return NN_ERROR() << "AHardwareBuffer memory not supported on host";
+GeneralResult<Memory> createSharedMemoryFromAHWB(const AHardwareBuffer& /*ahwb*/) {
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT)
+ << "AHardwareBuffer memory not supported on host";
}
-Result<Mapping> map(const Memory& memory) {
+GeneralResult<Mapping> map(const Memory& memory) {
if (memory.name == "ashmem") {
return mapAshmem(memory);
}
if (memory.name == "mmap_fd") {
return mapMemFd(memory);
}
- return NN_ERROR() << "Cannot map unknown memory " << memory.name;
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map unknown memory " << memory.name;
}
bool flush(const Mapping& mapping) {
diff --git a/nn/common/TypeUtils.cpp b/nn/common/TypeUtils.cpp
index ad17d9444..6d089bd2a 100644
--- a/nn/common/TypeUtils.cpp
+++ b/nn/common/TypeUtils.cpp
@@ -782,6 +782,20 @@ std::ostream& operator<<(std::ostream& os, const TimePoint& timePoint) {
return os << timePoint.time_since_epoch().count() << "ns since epoch";
}
+std::ostream& operator<<(std::ostream& os, const SyncFence::FenceState& fenceState) {
+ switch (fenceState) {
+ case SyncFence::FenceState::ACTIVE:
+ return os << "ACTIVE";
+ case SyncFence::FenceState::SIGNALED:
+ return os << "SIGNALED";
+ case SyncFence::FenceState::ERROR:
+ return os << "ERROR";
+ case SyncFence::FenceState::UNKNOWN:
+ return os << "UNKNOWN";
+ }
+ return os << "SyncFence::FenceState{" << underlyingType(fenceState) << "}";
+}
+
std::ostream& operator<<(std::ostream& os, const OptionalTimePoint& optionalTimePoint) {
if (!optionalTimePoint.has_value()) {
return os << "<no time point>";
diff --git a/nn/common/Types.cpp b/nn/common/Types.cpp
index 761ffa72c..17485f442 100644
--- a/nn/common/Types.cpp
+++ b/nn/common/Types.cpp
@@ -17,6 +17,9 @@
#include "Types.h"
#include <android-base/logging.h>
+#include <cutils/native_handle.h>
+#include <errno.h>
+#include <poll.h>
#include <algorithm>
#include <cstddef>
@@ -121,4 +124,66 @@ Capabilities::OperandPerformanceTable::asVector() const {
return mSorted;
}
+SyncFence SyncFence::createAsSignaled() {
+ return SyncFence(nullptr);
+}
+
+Result<SyncFence> SyncFence::create(NativeHandle syncFence) {
+ const bool isValid = (syncFence != nullptr && syncFence->handle() != nullptr &&
+ syncFence->handle()->numFds == 1 && syncFence->handle()->numInts == 0 &&
+ &syncFence->handle()->data[0] != nullptr);
+ if (!isValid) {
+ return NN_ERROR() << "Invalid sync fence handle passed to SyncFence::create";
+ }
+ return SyncFence(std::move(syncFence));
+}
+
+SyncFence::SyncFence(NativeHandle syncFence) : mSyncFence(std::move(syncFence)) {}
+
+SyncFence::FenceState SyncFence::syncWait(OptionalTimeout optionalTimeout) const {
+ if (mSyncFence == nullptr) {
+ return FenceState::SIGNALED;
+ }
+
+ const int fd = mSyncFence->handle()->data[0];
+ const int timeout = optionalTimeout.value_or(Timeout{-1}).count();
+
+ // This implementation is directly based on the ::sync_wait() implementation.
+
+ struct pollfd fds;
+ int ret;
+
+ if (fd < 0) {
+ errno = EINVAL;
+ return FenceState::UNKNOWN;
+ }
+
+ fds.fd = fd;
+ fds.events = POLLIN;
+
+ do {
+ ret = poll(&fds, 1, timeout);
+ if (ret > 0) {
+ if (fds.revents & POLLNVAL) {
+ errno = EINVAL;
+ return FenceState::UNKNOWN;
+ }
+ if (fds.revents & POLLERR) {
+ errno = EINVAL;
+ return FenceState::ERROR;
+ }
+ return FenceState::SIGNALED;
+ } else if (ret == 0) {
+ errno = ETIME;
+ return FenceState::ACTIVE;
+ }
+ } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+ return FenceState::UNKNOWN;
+}
+
+NativeHandle SyncFence::getHandle() const {
+ return mSyncFence;
+}
+
} // namespace android::nn
diff --git a/nn/common/include/nnapi/IBuffer.h b/nn/common/include/nnapi/IBuffer.h
new file mode 100644
index 000000000..295681ecd
--- /dev/null
+++ b/nn/common/include/nnapi/IBuffer.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IBUFFER_H
+#define ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IBUFFER_H
+
+#include "nnapi/Result.h"
+#include "nnapi/Types.h"
+
+namespace android::nn {
+
+/**
+ * This interface represents a device memory buffer.
+ *
+ * This interface is thread-safe, and any class that implements this interface must be thread-safe.
+ */
+class IBuffer {
+ public:
+ /**
+ * Retrieves the token corresponding to this buffer.
+ *
+ * @return MemoryDomainToken corresponding to this buffer.
+ */
+ virtual Request::MemoryDomainToken getToken() const = 0;
+
+ /**
+ * Retrieves the content of this buffer to a shared memory region.
+ *
+ * The IBuffer object must have been initialized before the call to IBuffer::copyTo.
+ * For more information on the state of the IBuffer object, refer to IDevice::allocate.
+ *
+ * @param dst The destination shared memory region.
+ * @return Nothing on success, otherwise GeneralError.
+ */
+ virtual GeneralResult<void> copyTo(const Memory& dst) const = 0;
+
+ /**
+ * Sets the content of this buffer from a shared memory region.
+ *
+ * @param src The source shared memory region.
+ * @param dimensions Updated dimensional information. If the dimensions of the IBuffer object
+ * are not fully specified, then the dimensions must be fully specified here. If the
+ * dimensions of the IBuffer object are fully specified, then the dimensions may be empty
+ * here. If dimensions.size() > 0, then all dimensions must be specified here, and any
+ * dimension that was specified in the IBuffer object must have the same value here.
+ * @return Nothing on success, otherwise GeneralError.
+ */
+ virtual GeneralResult<void> copyFrom(const Memory& src, const Dimensions& dimensions) const = 0;
+
+ // Public virtual destructor to allow objects to be stored (and destroyed) as smart pointers.
+ // E.g., std::unique_ptr<IBuffer>.
+ virtual ~IBuffer() = default;
+
+ protected:
+ // Protect the non-destructor special member functions to prevent object slicing.
+ IBuffer() = default;
+ IBuffer(const IBuffer&) = default;
+ IBuffer(IBuffer&&) noexcept = default;
+ IBuffer& operator=(const IBuffer&) = default;
+ IBuffer& operator=(IBuffer&&) noexcept = default;
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IBUFFER_H
diff --git a/nn/common/include/nnapi/IDevice.h b/nn/common/include/nnapi/IDevice.h
new file mode 100644
index 000000000..9a2e51680
--- /dev/null
+++ b/nn/common/include/nnapi/IDevice.h
@@ -0,0 +1,367 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IDEVICE_H
+#define ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IDEVICE_H
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "nnapi/Result.h"
+#include "nnapi/Types.h"
+
+namespace android::nn {
+
+// Forward declarations
+class IBuffer;
+class IPreparedModel;
+
+/**
+ * This interface represents a device driver.
+ *
+ * This interface is thread-safe, and any class that implements this interface must be thread-safe.
+ */
+class IDevice {
+ public:
+ /**
+ * Returns the name of the driver.
+ *
+ * @return Name of the driver.
+ */
+ virtual const std::string& getName() const = 0;
+
+ /**
+ * Get the version string of the driver implementation.
+ *
+ * The version string must be a unique token among the set of version strings of drivers of a
+ * specific device. The token identifies the device driver's implementation. The token must not
+ * be confused with the feature level which is solely defined by the interface version. This API
+ * is opaque to the Android framework, but the Android framework may use the information for
+ * debugging or to pass on to NNAPI applications.
+ *
+ * Application developers sometimes have specific requirements to ensure good user experiences,
+ * and they need more information to make intelligent decisions when the Android framework
+ * cannot. For example, combined with the device name and other information, the token can help
+ * NNAPI applications filter devices based on their needs:
+ * - An application demands a certain level of performance, but a specific version of the driver
+ * cannot meet that requirement because of a performance regression. The application can
+ * disallow the driver based on the version provided.
+ * - An application has a minimum precision requirement, but certain versions of the driver
+ * cannot meet that requirement because of bugs or certain optimizations. The application can
+ * filter out versions of these drivers.
+ *
+ * @return version The version string of the device implementation. Must have nonzero length.
+ */
+ virtual const std::string& getVersionString() const = 0;
+
+ /**
+ * Returns the feature level of a driver.
+ *
+ * @return featureLevel The API level of the most advanced feature this driver implements.
+ */
+ virtual Version getFeatureLevel() const = 0;
+
+ /**
+ * Returns the device type of a driver.
+ *
+ * The device type can be used to help application developers to distribute Machine Learning
+ * workloads and other workloads such as graphical rendering. E.g., for an app which renders AR
+ * scenes based on real time object detection results, the developer could choose an ACCELERATOR
+ * type device for ML workloads, and reserve GPU for graphical rendering.
+ *
+ * @return type The DeviceType of the device. Please note, this is not a bitfield of
+ * DeviceTypes. Each device must only be of a single DeviceType.
+ */
+ virtual DeviceType getType() const = 0;
+
+ /**
+ * Gets information about extensions supported by the driver implementation.
+ *
+ * Extensions of category ExtensionCategory::BASE must not appear in the list.
+ *
+ * All extension operations and operands must be fully supported for the extension to appear in
+ * the list of supported extensions.
+ *
+ * @return extensions A list of supported extensions.
+ */
+ virtual const std::vector<Extension>& getSupportedExtensions() const = 0;
+
+ /**
+ * Gets the capabilities of a driver.
+ *
+ * @return capabilities Capabilities of the driver.
+ */
+ virtual const Capabilities& getCapabilities() const = 0;
+
+ /**
+ * Gets the caching requirements of the driver implementation.
+ *
+ * There are two types of cache file descriptors provided to the driver: model cache and data
+ * cache.
+ *
+ * The data cache is for caching constant data, possibly including preprocessed and transformed
+ * tensor buffers. Any modification to the data cache should have no worse effect than
+ * generating bad output values at execution time.
+ *
+ * The model cache is for caching security-sensitive data such as compiled executable machine
+ * code in the device's native binary format. A modification to the model cache may affect the
+ * driver's execution behavior, and a malicious client could make use of this to execute beyond
+ * the granted permission. Thus, the driver must always check whether the model cache is
+ * corrupted before preparing the model from cache.
+ *
+ * IDevice::getNumberOfCacheFilesNeeded returns how many of each type of cache files the driver
+ * implementation needs to cache a single prepared model. Returning 0 for both types indicates
+ * compilation caching is not supported by this driver. The driver may still choose not to cache
+ * certain compiled models even if it reports that caching is supported.
+ *
+ * If the device reports that caching is not supported, the user may avoid calling
+ * IDevice::prepareModelFromCache or providing cache file descriptors to IDevice::prepareModel.
+ *
+ * @return A pair of:
+ * - numModelCache An unsigned integer indicating how many files for model cache the driver
+ * needs to cache a single prepared model. It must be less than or equal to
+ * ::android::nn::kMaxNumberOfCacheFiles.
+ * - numDataCache An unsigned integer indicating how many files for data cache the driver
+ * needs to cache a single prepared model. It must be less than or equal to
+ * ::android::nn::kMaxNumberOfCacheFiles.
+ */
+ virtual std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const = 0;
+
+ /**
+ * Blocks until the device is not in a bad state.
+ *
+ * @return Nothing on success, otherwise GeneralError.
+ */
+ virtual GeneralResult<void> wait() const = 0;
+
+ /**
+ * Gets the supported operations in a model.
+ *
+ * IDevice::getSupportedOperations indicates which operations of the top-level subgraph are
+ * fully supported by the vendor driver. If an operation may not be supported for any reason,
+ * IDevice::getSupportedOperations must return `false` for that operation.
+ *
+ * The {@link OperationType::IF} and {@link OperationType::WHILE} operations may only be fully
+ * supported if the vendor driver fully supports all operations in the referenced subgraphs.
+ *
+ * @param model A Model whose operations--and their corresponding operands--are to be verified
+ * by the driver.
+ * @return supportedOperations A list of supported operations, where `true` indicates the
+ * operation is supported and `false` indicates the operation is not supported. The index of
+ * "supported" corresponds with the index of the operation it is describing.
+ */
+ virtual GeneralResult<std::vector<bool>> getSupportedOperations(const Model& model) const = 0;
+
+ /**
+ * Creates a prepared model for execution.
+ *
+ * IDevice::prepareModel is used to make any necessary transformations or alternative
+ * representations to a model for execution, possibly including transformations on the constant
+ * data, optimization on the model's graph, or compilation into the device's native binary
+ * format. The model itself is not changed.
+ *
+ * Optionally, caching information may be provided for the driver to save the prepared model to
+ * cache files for faster model compilation time when the same model preparation is requested in
+ * the future. There are two types of cache file handles provided to the driver: model cache and
+ * data cache. For more information on the two types of cache handles, refer to
+ * IDevice::getNumberOfCacheFilesNeeded.
+ *
+ * The file descriptors must be opened with read and write permission. A file may have any size,
+ * and the corresponding file descriptor may have any offset. The driver must truncate a file to
+ * zero size before writing to that file. The file descriptors may be closed by the client once
+ * the preparation has finished. The driver must dup a file descriptor if it wants to get access
+ * to the cache file later.
+ *
+ * IDevice::prepareModel must verify its inputs related to preparing the model (as opposed to
+ * saving the prepared model to cache) are correct. If there is an error, IDevice::prepareModel
+ * must immediately return {@link ErrorStatus::INVALID_ARGUMENT} as a GeneralError. If the
+ * inputs to IDevice::prepareModel are valid and there is no error, IDevice::prepareModel must
+ * prepare the model.
+ *
+ * The model is prepared with a priority. This priority is relative to other prepared models
+ * owned by the same client. Higher priority executions may use more compute resources than
+ * lower priority executions, and may preempt or starve lower priority executions.
+ *
+ * IDevice::prepareModel can be called with an optional deadline. If the model is not able to be
+ * prepared before the provided deadline, the model preparation may be aborted, and either
+ * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned as a GeneralError.
+ *
+ * Optionally, the driver may save the prepared model to cache during the preparation. Any error
+ * that occurs when saving to cache must not affect the status of preparing the model. Even if
+ * the input arguments related to the cache may be invalid, or the driver may fail to save to
+ * cache, IDevice::prepareModel must finish preparing the model. The driver may choose not to
+ * save to cache even if the caching information is provided and valid.
+ *
+ * The only information that may be unknown to the model at this stage is the shape of the
+ * tensors, which may only be known at execution time. As such, some driver services may return
+ * partially prepared models, where the prepared model may only be finished when it is paired
+ * with a set of inputs to the model. Note that the same prepared model object may be used with
+ * different shapes of inputs on different (possibly concurrent) executions.
+ *
+ * @param model The model to be prepared for execution.
+ * @param preference Indicates the intended execution behavior of a prepared model.
+ * @param priority Priority of the prepared model relative to other prepared models owned by an
+ * application.
+ * @param deadline Optional time point. If provided, prepareModel is expected to complete by
+ * this time point. If it is not able to be completed by the deadline, the execution may be
+ * aborted.
+ * @param modelCache A vector of handles with each entry holding exactly one cache file
+ * descriptor for the security-sensitive cache. The length of the vector must either be 0
+ * indicating that caching information is not provided, or match numModelCache returned from
+ * IDevice::getNumberOfCacheFilesNeeded. The cache handles will be provided in the same
+ * order when retrieving the preparedModel from cache files with
+ * IDevice::prepareModelFromCache.
+ * @param dataCache A vector of handles with each entry holding exactly one cache file
+ * descriptor for the constants' cache. The length of the vector must either be 0 indicating
+ * that caching information is not provided, or match numDataCache returned from
+ * IDevice::getNumberOfCacheFilesNeeded. The cache handles will be provided in the same
+ * order when retrieving the preparedModel from cache files with
+ * IDevice::prepareModelFromCache.
+ * @param token An caching token of length ::android::nn::kByteSizeOfCacheToken identifying the
+ * prepared model. The same token will be provided when retrieving the prepared model from
+ * the cache files with IDevice::prepareModelFromCache. Tokens should be chosen to have a
+ * low rate of collision for a particular application. The driver cannot detect a collision;
+ * a collision will result in a failed execution or in a successful execution that produces
+ * incorrect output values. If both modelCache and dataCache are empty indicating that
+ * caching information is not provided, this token must be ignored.
+ * @return preparedModel An IPreparedModel object representing a model that has been prepared
+ * for execution, otherwise GeneralError.
+ */
+ virtual GeneralResult<SharedPreparedModel> prepareModel(
+ const Model& model, ExecutionPreference preference, Priority priority,
+ OptionalTimePoint deadline, const std::vector<NativeHandle>& modelCache,
+ const std::vector<NativeHandle>& dataCache, const CacheToken& token) const = 0;
+
+ /**
+ * Creates a prepared model from cache files for execution.
+ *
+ * IDevice::prepareModelFromCache is used to retrieve a prepared model directly from cache files
+ * to avoid slow model compilation time. There are two types of cache file handles provided to
+ * the driver: model cache and data cache. For more information on the two types of cache
+ * handles, refer to IDevice::getNumberOfCacheFilesNeeded.
+ *
+ * The file descriptors must be opened with read and write permission. A file may have any size,
+ * and the corresponding file descriptor may have any offset. The driver must truncate a file to
+ * zero size before writing to that file. The file descriptors may be closed by the client once
+ * the preparation has finished. The driver must dup a file descriptor if it wants to get access
+ * to the cache file later.
+ *
+ * IDevice::prepareModelFromCache must verify its inputs are correct, and that the
+ * security-sensitive cache has not been modified since it was last written by the driver. If
+ * there is an error, or if compilation caching is not supported, or if the security-sensitive
+ * cache has been modified, IDevice::prepareModelFromCache must immediately return {@link
+ * ErrorStatus::INVALID_ARGUMENT} as a GeneralError. If the inputs to
+ * IDevice::prepareModelFromCache are valid, the security-sensitive cache is not modified, and
+ * there is no error, IDevice::prepareModelFromCache must prepare the model
+ *
+ * IDevice::prepareModelFromCache can be called with an optional deadline. If the model is not
+ * able to prepared before the provided deadline, the model preparation may be aborted, and
+ * either {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned as a GeneralError.
+ *
+ * The only information that may be unknown to the model at this stage is the shape of the
+ * tensors, which may only be known at execution time. As such, some driver services may return
+ * partially prepared models, where the prepared model may only be finished when it is paired
+ * with a set of inputs to the model. Note that the same prepared model object may be used with
+ * different shapes of inputs on different (possibly concurrent) executions.
+ *
+ * @param deadline Optional time point. If provided, prepareModel is expected to complete by
+ * this time point. If it is not able to be completed by the deadline, the execution may be
+ * aborted.
+ * @param modelCache A vector of handles with each entry holding exactly one cache file
+ * descriptor for the security-sensitive cache. The length of the vector must match the
+ * numModelCache returned from IDevice::getNumberOfCacheFilesNeeded. The cache handles will
+ * be provided in the same order as with IDevice::prepareModel.
+ * @param dataCache A vector of handles with each entry holding exactly one cache file
+ * descriptor for the constants' cache. The length of the vector must match the numDataCache
+ * returned from IDevice::getNumberOfCacheFilesNeeded. The cache handles will be provided in
+ * the same order as with IDevice::prepareModel.
+ * @param token A caching token of length ::android::nn::kByteSizeOfCacheToken identifying the
+ * prepared model. It is the same token provided when saving the cache files with
+ * IDevice::prepareModel. Tokens should be chosen to have a low rate of collision for a
+ * particular application. The driver cannot detect a collision; a collision will result in
+ * a failed execution or in a successful execution that produces incorrect output values.
+ * @return preparedModel An IPreparedModel object representing a model that has been prepared
+ * for execution, otherwise GeneralError.
+ */
+ virtual GeneralResult<SharedPreparedModel> prepareModelFromCache(
+ OptionalTimePoint deadline, const std::vector<NativeHandle>& modelCache,
+ const std::vector<NativeHandle>& dataCache, const CacheToken& token) const = 0;
+
+ /**
+ * Allocates a driver-managed buffer with the properties specified by the descriptor as well as
+ * the input and output roles of prepared models.
+ *
+ * IDevice::allocate must verify its inputs are correct. If there is an error, or if a certain
+ * role or property is not supported by the driver, IDevice::allocate must return with {@link
+ * ErrorStatus::INVALID_ARGUMENT} as a GeneralError. If the allocation is successful, this
+ * method must return the produced IBuffer. A successful allocation must accommodate all of the
+ * specified roles and buffer properties.
+ *
+ * The buffer is allocated as an uninitialized state. An uninitialized buffer may only be used
+ * in ways that are specified by outputRoles. A buffer is initialized after it is used as an
+ * output in a successful execution, or after a successful invocation of IBuffer::copyFrom on
+ * the buffer. An initialized buffer may be used according to all roles specified in inputRoles
+ * and outputRoles. A buffer will return to the uninitialized state if it is used as an output
+ * in a failed execution, or after a failed invocation of IBuffer::copyFrom on the buffer.
+ *
+ * The driver may deduce the dimensions of the buffer according to the buffer descriptor as well
+ * as the input and output roles. The dimensions or rank of the buffer may be unknown at this
+ * stage. As such, some driver services may only create a placeholder and defer the actual
+ * allocation until execution time. Note that the same buffer may be used for different shapes
+ * of outputs on different executions. When the buffer is used as an input, the input shape must
+ * be the same as the output shape from the last execution using this buffer as an output.
+ *
+ * The driver must apply proper validatation upon every usage of the buffer, and fail the
+ * execution immediately if the usage is illegal.
+ *
+ * @param desc A buffer descriptor specifying the properties of the buffer to allocate.
+ * @param preparedModels A vector of IPreparedModel objects. Must only contain IPreparedModel
+ * objects from the same IDevice as this method invoked on.
+ * @param inputRoles A vector of roles with each specifying an input to a prepared model.
+ * @param outputRoles A vector of roles with each specifying an output to a prepared model.
+ * Each role specified in inputRoles and outputRoles must be unique. The corresponding model
+ * operands of the roles must have the same OperandType, scale, zero point, and ExtraParams.
+ * The dimensions of the operands and the dimensions specified in the buffer descriptor must
+ * be compatible with each other. Two dimensions are incompatible if there is at least one
+ * axis that is fully specified in both but has different values.
+ * @return The allocated IBuffer object. If the buffer was unable to be allocated due to an
+ * error, a GeneralError is returned instead.
+ */
+ virtual GeneralResult<SharedBuffer> allocate(
+ const BufferDesc& desc, const std::vector<SharedPreparedModel>& preparedModels,
+ const std::vector<BufferRole>& inputRoles,
+ const std::vector<BufferRole>& outputRoles) const = 0;
+
+ // Public virtual destructor to allow objects to be stored (and destroyed) as smart pointers.
+ // E.g., std::unique_ptr<IDevice>.
+ virtual ~IDevice() = default;
+
+ protected:
+ // Protect the non-destructor special member functions to prevent object slicing.
+ IDevice() = default;
+ IDevice(const IDevice&) = default;
+ IDevice(IDevice&&) noexcept = default;
+ IDevice& operator=(const IDevice&) = default;
+ IDevice& operator=(IDevice&&) noexcept = default;
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IDEVICE_H
diff --git a/nn/common/include/nnapi/IPreparedModel.h b/nn/common/include/nnapi/IPreparedModel.h
new file mode 100644
index 000000000..07476e276
--- /dev/null
+++ b/nn/common/include/nnapi/IPreparedModel.h
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IPREPARED_MODEL_H
+#define ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IPREPARED_MODEL_H
+
+#include <any>
+#include <functional>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "nnapi/Types.h"
+
+namespace android::nn {
+
+// Returns status, timingLaunched, timingFenced
+using ExecuteFencedInfoCallback = std::function<GeneralResult<std::pair<Timing, Timing>>()>;
+
+/**
+ * IPreparedModel describes a model that has been prepared for execution and is used to launch
+ * executions.
+ *
+ * This interface is thread-safe, and any class that implements this interface must be thread-safe.
+ */
+class IPreparedModel {
+ public:
+ /**
+ * Performs a synchronous execution on a prepared model.
+ *
+ * The execution is performed synchronously with respect to the caller. IPreparedModel::execute
+ * must verify the inputs to the function are correct. If there is an error,
+ * IPreparedModel::execute must immediately return {@link ErrorStatus::INVALID_ARGUMENT} as a
+ * ExecutionError. If the inputs to the function are valid and there is no error,
+ * IPreparedModel::execute must perform the execution, and must not return until the execution
+ * is complete.
+ *
+ * The caller must not change the content of any data object referenced by request (described by
+ * the {@link DataLocation} of a {@link RequestArgument}) until IPreparedModel::execute returns.
+ * IPreparedModel::execute must not change the content of any of the data objects corresponding
+ * to request inputs.
+ *
+ * If the prepared model was prepared from a model wherein all tensor operands have fully
+ * specified dimensions, and the inputs to the function are valid, and at execution time every
+ * operation's input operands have legal values, then the execution should complete
+ * successfully. There must be no failure unless the device itself is in a bad state.
+ *
+ * IPreparedModel::execute may be called with an optional deadline. If the execution is not
+ * able to be completed before the provided deadline, the execution may be aborted, and either
+ * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned as a ExecutionError.
+ *
+ * @param request The input and output information on which the prepared model is to be
+ * executed.
+ * @param measure Specifies whether or not to measure duration of the execution.
+ * @param deadline Optional time point. If provided, execute is expected to complete by this
+ * time point. If it is not able to be completed by the deadline, the execution may be
+ * aborted.
+ * @param loopTimeoutDuration The maximum amount of time that should be spent executing a {@link
+ * OperationType::WHILE} operation. If a loop condition model does not output `false` within
+ * this duration, the execution must be aborted. If no loop timeout duration is provided,
+ * the maximum amount of time is {@link LoopTimeoutDurationNs::DEFAULT}. When provided, the
+ * duration must not exceed {@link LoopTimeoutDurationNs::MAXIMUM}.
+ * @return A pair consisting of:
+ * - A list of shape information of model output operands. The index into "outputShapes"
+ * corresponds to the index of the output operand in the Request outputs vector.
+ * outputShapes must be empty unless the execution is successful or the ExecutionResult is
+ * {@link ErrorStatus::OUTPUT_INSUFFICIENT_SIZE}. outputShapes may be empty if the
+ * execution is successful and all model output operands are fully-specified at execution
+ * time. outputShapes must have the same number of elements as the number of model output
+ * operands if the ExecutionResult is {@link ErrorStatus::OUTPUT_INSUFFICIENT_SIZE}, or if
+ * the execution is successful and the model has at least one output operand that is not
+ * fully-specified.
+ * - Duration of execution. Unless measure is YES and the execution is successful, all times
+ * must be reported as UINT64_MAX. A driver may choose to report any time as UINT64_MAX,
+ * indicating that measurement is not available.
+ */
+ virtual ExecutionResult<std::pair<std::vector<OutputShape>, Timing>> execute(
+ const Request& request, MeasureTiming measure, const OptionalTimePoint& deadline,
+ const OptionalTimeoutDuration& loopTimeoutDuration) const = 0;
+
+ /**
+ * Launch a fenced asynchronous execution on a prepared model.
+ *
+ * The execution is performed asynchronously with respect to the caller.
+ * IPreparedModel::executeFenced must verify its inputs are correct, and the usages of memory
+ * pools allocated by IDevice::allocate are valid. If there is an error,
+ * IPreparedModel::executeFenced must immediately return {@link ErrorStatus::INVALID_ARGUMENT}
+ * as a GeneralError. If the inputs to the function are valid and there is no error,
+ * IPreparedModel::executeFenced must dispatch an asynchronous task to perform the execution in
+ * the background, and immediately return with a sync fence that will be signaled once the
+ * execution is completed and a callback that can be used by the client to query the duration
+ * and runtime error status. If the task has finished before the call returns, an empty handle
+ * may be returned for syncFence. The execution must wait for all the sync fences (if any) in
+ * waitFor to be signaled before starting the actual execution.
+ *
+ * When the asynchronous task has finished its execution, it must immediately signal the
+ * syncFence returned from the IPreparedModel::executeFenced call. After the syncFence is
+ * signaled, the task must not modify the content of any data object referenced by request
+ * (described by the {@link DataLocation} of a {@link Request::Argument}).
+ *
+ * IPreparedModel::executeFenced may be called with an optional deadline and an optional
+ * duration. If the execution is not able to be completed before the provided deadline or within
+ * the timeout duration (measured from when all sync fences in waitFor are signaled), whichever
+ * comes earlier, the execution may be aborted, and either {@link
+ * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT}
+ * may be returned as an GeneralError. The error due to an abort must be sent the same way as
+ * other errors, described above.
+ *
+ * If any of the sync fences in waitFor changes to error status after the
+ * IPreparedModel::executeFenced call succeeds, or the execution is aborted because it cannot
+ * finish before the deadline has been reached or the duration has elapsed, the driver must
+ * immediately set the returned syncFence to error status.
+ *
+ * @param request The input and output information on which the prepared model is to be
+ * executed.
+ * @param waitFor A vector of sync fence file descriptors. The execution must wait for all sync
+ * fence to be signaled before starting the task.
+ * @param measure Specifies whether or not to measure duration of the execution.
+ * @param deadline The time by which execution is expected to complete. If the execution cannot
+ * be finished by the deadline, the execution may be aborted.
+ * @param loopTimeoutDuration The maximum amount of time that should be spent executing a {@link
+ * OperationType::WHILE} operation. If a loop condition model does not output `false` within
+ * this duration, the execution must be aborted. If no loop timeout duration is provided,
+ * the maximum amount of time is {@link LoopTimeoutDurationNs::DEFAULT}. When provided, the
+ * duration must not exceed {@link LoopTimeoutDurationNs::MAXIMUM}.
+ * @param timeoutDurationAfterFence The timeout duration within which the execution is expected
+ * to complete after all sync fences in waitFor are signaled.
+ * @return A pair consisting of:
+ * - A syncFence that will be triggered when the task is completed. The syncFence will be
+ * set to error if critical error occurs when doing actual evaluation.
+ * - A callback can be used to query information like duration and detailed runtime error
+ * status when the task is completed.
+ */
+ virtual GeneralResult<std::pair<SyncFence, ExecuteFencedInfoCallback>> executeFenced(
+ const Request& request, const std::vector<SyncFence>& waitFor, MeasureTiming measure,
+ const OptionalTimePoint& deadline, const OptionalTimeoutDuration& loopTimeoutDuration,
+ const OptionalTimeoutDuration& timeoutDurationAfterFence) const = 0;
+
+ /**
+ * Return the resource that the IPreparedModel wraps, or any empty std::any.
+ *
+ * This method is used for IDevice::allocate.
+ *
+ * @return std::any containing the underlying resource.
+ */
+ virtual std::any getUnderlyingResource() const = 0;
+
+ // Public virtual destructor to allow objects to be stored (and destroyed) as smart pointers.
+ // E.g., std::unique_ptr<IPreparedModel>.
+ virtual ~IPreparedModel() = default;
+
+ protected:
+ // Protect the non-destructor special member functions to prevent object slicing.
+ IPreparedModel() = default;
+ IPreparedModel(const IPreparedModel&) = default;
+ IPreparedModel(IPreparedModel&&) noexcept = default;
+ IPreparedModel& operator=(const IPreparedModel&) = default;
+ IPreparedModel& operator=(IPreparedModel&&) noexcept = default;
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IPREPARED_MODEL_H
diff --git a/nn/common/include/nnapi/Result.h b/nn/common/include/nnapi/Result.h
index e232cef3f..157eb7845 100644
--- a/nn/common/include/nnapi/Result.h
+++ b/nn/common/include/nnapi/Result.h
@@ -22,6 +22,7 @@
#include <optional>
#include <sstream>
#include <string>
+#include <tuple>
#include <utility>
namespace android::nn {
@@ -38,11 +39,19 @@ using Result = base::expected<Type, std::string>;
namespace detail {
+template <typename... Ts>
class ErrorBuilder {
public:
+ template <typename... Us>
+ explicit ErrorBuilder(Us&&... args) : mArgs(std::forward<Us>(args)...) {}
+
template <typename T, typename E>
- operator base::expected<T, E>() const /* NOLINT(google-explicit-constructor) */ {
- return base::unexpected<E>(std::move(mStream).str());
+ operator base::expected<T, E>() /* NOLINT(google-explicit-constructor) */ {
+ return std::apply(
+ [this](Ts&&... args) {
+ return base::unexpected<E>(E{std::move(mStream).str(), std::move(args)...});
+ },
+ std::move(mArgs));
}
template <typename T>
@@ -52,6 +61,7 @@ class ErrorBuilder {
}
private:
+ std::tuple<Ts...> mArgs;
std::ostringstream mStream;
};
@@ -60,8 +70,9 @@ class ErrorBuilder {
/**
* Creates an error builder for the case where no arguments are provided.
*/
-inline detail::ErrorBuilder error() {
- return detail::ErrorBuilder();
+template <typename... Types>
+inline detail::ErrorBuilder<std::decay_t<Types>...> error(Types&&... args) {
+ return detail::ErrorBuilder<std::decay_t<Types>...>(std::forward<Types>(args)...);
}
/**
@@ -80,7 +91,7 @@ inline detail::ErrorBuilder error() {
* return <regular_return_value>;
*/
#define NN_ERROR(...) \
- [] { \
+ [&] { \
using ::android::nn::error; \
return error(__VA_ARGS__) << __FILE__ << ":" << __LINE__ << ": "; \
}()
diff --git a/nn/common/include/nnapi/SharedMemory.h b/nn/common/include/nnapi/SharedMemory.h
index ee8330f45..1c8156b2a 100644
--- a/nn/common/include/nnapi/SharedMemory.h
+++ b/nn/common/include/nnapi/SharedMemory.h
@@ -43,7 +43,7 @@ class MutableMemoryBuilder {
DataLocation append(size_t length);
bool empty() const;
- Result<Memory> finish();
+ GeneralResult<Memory> finish();
private:
uint32_t mPoolIndex;
@@ -57,7 +57,7 @@ class ConstantMemoryBuilder {
DataLocation append(const void* data, size_t length);
bool empty() const;
- Result<Memory> finish();
+ GeneralResult<Memory> finish();
private:
struct LazyCopy {
@@ -70,13 +70,13 @@ class ConstantMemoryBuilder {
std::vector<LazyCopy> mSlices;
};
-Result<Memory> createSharedMemory(size_t size);
+GeneralResult<Memory> createSharedMemory(size_t size);
-Result<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset);
+GeneralResult<Memory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset);
-Result<Memory> createSharedMemoryFromHidlMemory(const hardware::hidl_memory& memory);
+GeneralResult<Memory> createSharedMemoryFromHidlMemory(const hardware::hidl_memory& memory);
-Result<Memory> createSharedMemoryFromAHWB(const AHardwareBuffer& ahwb);
+GeneralResult<Memory> createSharedMemoryFromAHWB(const AHardwareBuffer& ahwb);
struct Mapping {
std::variant<void*, const void*> pointer;
@@ -84,7 +84,7 @@ struct Mapping {
std::any context;
};
-Result<Mapping> map(const Memory& memory);
+GeneralResult<Mapping> map(const Memory& memory);
bool flush(const Mapping& mapping);
diff --git a/nn/common/include/nnapi/TypeUtils.h b/nn/common/include/nnapi/TypeUtils.h
index 56b62f9b2..6b2af916f 100644
--- a/nn/common/include/nnapi/TypeUtils.h
+++ b/nn/common/include/nnapi/TypeUtils.h
@@ -100,6 +100,7 @@ std::ostream& operator<<(std::ostream& os, const BufferRole& bufferRole);
std::ostream& operator<<(std::ostream& os, const Request::Argument& requestArgument);
std::ostream& operator<<(std::ostream& os, const Request::MemoryPool& memoryPool);
std::ostream& operator<<(std::ostream& os, const Request& request);
+std::ostream& operator<<(std::ostream& os, const SyncFence::FenceState& fenceState);
std::ostream& operator<<(std::ostream& os, const TimePoint& timePoint);
std::ostream& operator<<(std::ostream& os, const OptionalTimePoint& optionalTimePoint);
std::ostream& operator<<(std::ostream& os, const TimeoutDuration& timeoutDuration);
diff --git a/nn/common/include/nnapi/Types.h b/nn/common/include/nnapi/Types.h
index 371b4a5c2..3b9725d27 100644
--- a/nn/common/include/nnapi/Types.h
+++ b/nn/common/include/nnapi/Types.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_TYPES_H
#define ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_TYPES_H
+#include <android-base/expected.h>
#include <utils/NativeHandle.h>
#include <utils/StrongPointer.h>
@@ -57,7 +58,7 @@ constexpr uint8_t kExtensionPrefixBits = 16;
using AlignedData = std::max_align_t;
using SharedBuffer = std::shared_ptr<const IBuffer>;
using SharedDevice = std::shared_ptr<const IDevice>;
-using PreparedModel = std::shared_ptr<const IPreparedModel>;
+using SharedPreparedModel = std::shared_ptr<const IPreparedModel>;
// Canonical types
@@ -116,6 +117,14 @@ enum class ErrorStatus {
DEAD_OBJECT = 10000,
};
+struct GeneralError {
+ std::string message;
+ ErrorStatus code = ErrorStatus::GENERAL_FAILURE;
+};
+
+template <typename Type>
+using GeneralResult = base::expected<Type, GeneralError>;
+
enum class FusedActivationFunc : int32_t {
NONE = 0,
RELU = 1,
@@ -133,6 +142,16 @@ struct OutputShape {
bool isSufficient = false;
};
+struct ExecutionError {
+ std::string message;
+ ErrorStatus code = ErrorStatus::GENERAL_FAILURE;
+ // OutputShapes for code == OUTPUT_INSUFFICIENT_SIZE
+ std::vector<OutputShape> outputShapes = {};
+};
+
+template <typename Type>
+using ExecutionResult = base::expected<Type, ExecutionError>;
+
struct Timing {
uint64_t timeOnDevice = kNoTiming;
uint64_t timeInDriver = kNoTiming;
@@ -290,6 +309,34 @@ struct Request {
std::vector<MemoryPool> pools;
};
+// Representation of sync_fence.
+class SyncFence {
+ public:
+ static SyncFence createAsSignaled();
+ static Result<SyncFence> create(NativeHandle syncFence);
+
+ // The function syncWait() has the same semantics as the system function
+ // ::sync_wait(), except that the syncWait() return value is semantically
+ // richer.
+ enum class FenceState {
+ ACTIVE, // fence has not been signaled
+ SIGNALED, // fence has been signaled
+ ERROR, // fence has been placed in the error state
+ UNKNOWN, // either bad argument passed to syncWait(), or internal error
+ };
+ using Timeout = std::chrono::duration<int, std::milli>;
+ using OptionalTimeout = std::optional<Timeout>;
+
+ FenceState syncWait(OptionalTimeout optionalTimeout) const;
+
+ NativeHandle getHandle() const;
+
+ private:
+ explicit SyncFence(NativeHandle syncFence);
+
+ NativeHandle mSyncFence;
+};
+
using Clock = std::chrono::steady_clock;
using TimePoint = std::chrono::time_point<Clock, std::chrono::nanoseconds>;
diff --git a/nn/common/include/nnapi/Validation.h b/nn/common/include/nnapi/Validation.h
index 09133957b..3eda174f6 100644
--- a/nn/common/include/nnapi/Validation.h
+++ b/nn/common/include/nnapi/Validation.h
@@ -69,10 +69,9 @@ using PreparedModelRole = std::tuple<const IPreparedModel*, IOType, uint32_t>;
// IMPORTANT: This function cannot validate dimensions and extraParams with extension operand type.
// Each driver should do their own validation of extension type dimensions and extraParams.
Result<Version> validateMemoryDesc(
- const BufferDesc& desc,
- const std::vector<std::shared_ptr<const IPreparedModel>>& preparedModels,
+ const BufferDesc& desc, const std::vector<SharedPreparedModel>& preparedModels,
const std::vector<BufferRole>& inputRoles, const std::vector<BufferRole>& outputRoles,
- const std::function<const Model*(const std::shared_ptr<const IPreparedModel>&)>& getModel,
+ const std::function<const Model*(const SharedPreparedModel&)>& getModel,
std::set<PreparedModelRole>* preparedModelRoles, Operand* combinedOperand);
Result<void> validateOperandSymmPerChannelQuantParams(