Make NNAPI Memory type more structured
This CL makes NNAPI memory more structured by replacing the pervasive
use of NativeHandle with the new, more specific memory types "Ashmem"
and "MmapFd". It changes the Memory type to be a std::variant of Ashmem
and MmapFd, in addition to (a cleaned up) HardwareBuffer. Finally, the
Memory type also continues to hold the alternative type "NativeHandle"
(now as an "Unknown" memory type) to ensure all previous types of
memory could still be held.
This change also updates the utility and testing code to account for the
changes to the Memory type.
Bug: 183118727
Test: mma
Test: NeuralNetworksTest_static
Change-Id: I7e2fea015a18b5e00d56bef924ebca4449d5b6dd
Merged-In: I7e2fea015a18b5e00d56bef924ebca4449d5b6dd
(cherry picked from commit d6496953292ba60c357417e7c9673ba31d9f84f5)
diff --git a/common/SharedMemoryAndroid.cpp b/common/SharedMemoryAndroid.cpp
index 120e3c9..2124e4a 100644
--- a/common/SharedMemoryAndroid.cpp
+++ b/common/SharedMemoryAndroid.cpp
@@ -48,20 +48,15 @@
namespace android::nn {
namespace {
-GeneralResult<SharedMemory> createSharedMemoryFromUniqueFd(base::unique_fd fd, size_t size,
- int prot, size_t offset) {
- CHECK_GT(size, 0u);
- CHECK(fd.ok());
-
- std::vector<base::unique_fd> fds;
- fds.push_back(std::move(fd));
-
- const auto [lowOffsetBits, highOffsetBits] = getIntsFromOffset(offset);
- std::vector<int> ints = {prot, lowOffsetBits, highOffsetBits};
-
- auto handle = Handle{.fds = std::move(fds), .ints = std::move(ints)};
- return std::make_shared<const Memory>(
- Memory{.handle = std::move(handle), .size = size, .name = "mmap_fd"});
+GeneralResult<SharedMemory> createSharedMemoryFromUniqueFd(size_t size, int prot,
+ base::unique_fd fd, size_t offset) {
+ auto handle = Memory::Fd{
+ .size = size,
+ .prot = prot,
+ .fd = std::move(fd),
+ .offset = offset,
+ };
+ return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
}
#ifndef NN_COMPATIBILITY_LIBRARY_BUILD
@@ -71,40 +66,18 @@
const char* const kAllocatorService = "ashmem";
-GeneralResult<hardware::hidl_handle> hidlHandleFromSharedHandle(const Handle& handle) {
- auto fds = NN_TRY(dupFds(handle.fds.begin(), handle.fds.end()));
-
- constexpr size_t kMaxInt = std::numeric_limits<int>::max();
- CHECK_LE(handle.fds.size(), kMaxInt);
- CHECK_LE(handle.ints.size(), kMaxInt);
- native_handle_t* nativeHandle = native_handle_create(static_cast<int>(handle.fds.size()),
- static_cast<int>(handle.ints.size()));
+GeneralResult<hardware::hidl_handle> hidlHandleFromUniqueFd(base::unique_fd fd) {
+ native_handle_t* nativeHandle = native_handle_create(1, 0);
if (nativeHandle == nullptr) {
- return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to create native_handle";
+ return NN_ERROR() << "Failed to create native_handle";
}
- for (size_t i = 0; i < fds.size(); ++i) {
- nativeHandle->data[i] = fds[i].release();
- }
- std::copy(handle.ints.begin(), handle.ints.end(), &nativeHandle->data[nativeHandle->numFds]);
+ nativeHandle->data[0] = fd.release();
hardware::hidl_handle hidlHandle;
hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true);
return hidlHandle;
}
-GeneralResult<Handle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
- if (handle == nullptr) {
- return NN_ERROR() << "sharedHandleFromNativeHandle was passed nullptr for handle";
- }
-
- auto fds = NN_TRY(dupFds(handle->data + 0, handle->data + handle->numFds));
-
- auto ints = std::vector<int>(handle->data + handle->numFds,
- handle->data + handle->numFds + handle->numInts);
-
- return Handle{.fds = std::move(fds), .ints = std::move(ints)};
-}
-
GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
static const auto allocator = IAllocator::getService(kAllocatorService);
CHECK_GT(size, 0u);
@@ -121,19 +94,28 @@
return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
<< "IAllocator::allocate returned an invalid (empty) memory object";
}
+ if (maybeMemory.handle()->numFds != 1) {
+ return NN_ERROR() << "IAllocator::allocate returned an invalid memory object with "
+ << maybeMemory.handle()->numFds << " numFds, but expected 1";
+ }
+ if (maybeMemory.handle()->numInts != 0) {
+ return NN_ERROR() << "IAllocator::allocate returned an invalid memory object with "
+ << maybeMemory.handle()->numInts << " numInts, but expected 0";
+ }
- CHECK_LE(maybeMemory.size(), std::numeric_limits<uint32_t>::max());
- return std::make_shared<const Memory>(Memory{
- .handle = NN_TRY(sharedHandleFromNativeHandle(maybeMemory.handle())),
- .size = static_cast<uint32_t>(maybeMemory.size()),
- .name = maybeMemory.name(),
- });
+ CHECK_LE(maybeMemory.size(), std::numeric_limits<size_t>::max());
+ const int fd = maybeMemory.handle()->data[0];
+
+ auto handle = Memory::Ashmem{
+ .fd = NN_TRY(dupFd(fd)),
+ .size = static_cast<size_t>(maybeMemory.size()),
+ };
+ return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
}
-GeneralResult<Mapping> mapAshmem(const Memory& memory) {
- const auto hidlMemory = hidl_memory(
- memory.name, NN_TRY(hidlHandleFromSharedHandle(std::get<Handle>(memory.handle))),
- memory.size);
+GeneralResult<Mapping> map(const Memory::Ashmem& memory) {
+ auto handle = NN_TRY(hidlHandleFromUniqueFd(NN_TRY(dupFd(memory.fd))));
+ const auto hidlMemory = hidl_memory("ashmem", std::move(handle), memory.size);
const auto mapping = mapMemory(hidlMemory);
if (mapping == nullptr) {
@@ -175,62 +157,83 @@
constexpr int prot = PROT_READ | PROT_WRITE;
constexpr size_t offset = 0;
- return createSharedMemoryFromUniqueFd(std::move(fd), size, prot, offset);
+ return createSharedMemoryFromUniqueFd(size, prot, std::move(fd), offset);
}
-GeneralResult<Mapping> mapAshmem(const Memory& /*memory*/) {
+GeneralResult<Mapping> map(const Memory::Ashmem& /*memory*/) {
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map ashmem memory";
}
#endif // NN_COMPATIBILITY_LIBRARY_BUILD
+size_t getSize(const Memory::Ashmem& memory) {
+ return memory.size;
+}
+
+size_t getSize(const Memory::Fd& memory) {
+ return memory.size;
+}
+
+size_t getSize(const Memory::HardwareBuffer& memory) {
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(memory.handle.get(), &desc);
+ return desc.format == AHARDWAREBUFFER_FORMAT_BLOB ? desc.width : 0;
+}
+
+size_t getSize(const Memory::Unknown& memory) {
+ return memory.size;
+}
+
struct MmapFdMappingContext {
int prot;
std::any context;
};
-GeneralResult<Mapping> mapMemFd(const Memory& memory) {
- const size_t size = memory.size;
- const Handle& handle = std::get<Handle>(memory.handle);
- const int fd = handle.fds.front().get();
- const int prot = handle.ints[0];
- const size_t offset = getOffsetFromInts(handle.ints[1], handle.ints[2]);
-
- std::shared_ptr<base::MappedFile> mapping = base::MappedFile::FromFd(fd, offset, size, prot);
+GeneralResult<Mapping> map(const Memory::Fd& memory) {
+ std::shared_ptr<base::MappedFile> mapping =
+ base::MappedFile::FromFd(memory.fd, memory.offset, memory.size, memory.prot);
if (mapping == nullptr) {
- return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Can't mmap the file descriptor.";
+ return NN_ERROR() << "Can't mmap the file descriptor.";
}
- void* data = mapping->data();
+ char* data = mapping->data();
- auto context = MmapFdMappingContext{.prot = prot, .context = std::move(mapping)};
- return Mapping{.pointer = data, .size = size, .context = std::move(context)};
+ const bool writable = (memory.prot & PROT_WRITE) != 0;
+ std::variant<const void*, void*> pointer;
+ if (writable) {
+ pointer = static_cast<void*>(data);
+ } else {
+ pointer = static_cast<const void*>(data);
+ }
+
+ auto context = MmapFdMappingContext{.prot = memory.prot, .context = std::move(mapping)};
+ return Mapping{.pointer = pointer, .size = memory.size, .context = std::move(context)};
}
-GeneralResult<Mapping> mapAhwbBlobMemory(const HardwareBufferHandle& memory) {
+GeneralResult<Mapping> map(const Memory::HardwareBuffer& memory) {
AHardwareBuffer_Desc desc;
- AHardwareBuffer_describe(memory.get(), &desc);
+ AHardwareBuffer_describe(memory.handle.get(), &desc);
- CHECK_EQ(desc.format, AHARDWAREBUFFER_FORMAT_BLOB);
+ if (desc.format != AHARDWAREBUFFER_FORMAT_BLOB) {
+ return NN_ERROR() << "Unable to map non-blob AHardwareBuffer memory";
+ }
const uint32_t size = desc.width;
void* data = nullptr;
- const auto status = AHardwareBuffer_lock(memory.get(), desc.usage, -1, nullptr, &data);
+ const auto status = AHardwareBuffer_lock(memory.handle.get(), desc.usage, -1, nullptr, &data);
if (status != /*NO_ERROR*/ 0) {
- return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
- << "Can't lock the AHardwareBuffer. Error: " << status;
+ return NN_ERROR() << "Can't lock the AHardwareBuffer. Error: " << status;
}
// Create shared scoped object to munmap.
auto scoped = base::make_scope_guard(
- [ahwb = memory.get()] { AHardwareBuffer_unlock(ahwb, nullptr); });
+ [ahwb = memory.handle.get()] { AHardwareBuffer_unlock(ahwb, nullptr); });
auto sharedScoped = std::make_shared<decltype(scoped)>(std::move(scoped));
return Mapping{.pointer = data, .size = size, .context = std::move(sharedScoped)};
}
-GeneralResult<Mapping> mapAhwbMemory(const Memory& /*memory*/) {
- return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
- << "Unable to map non-BLOB AHardwareBuffer memory";
+GeneralResult<Mapping> map(const Memory::Unknown& /*memory*/) {
+ return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map Unknown memory";
}
void freeHardwareBuffer(AHardwareBuffer* buffer) {
@@ -243,15 +246,6 @@
} // namespace
-HardwareBufferHandle::HardwareBufferHandle(AHardwareBuffer* handle, bool takeOwnership)
- : mHandle(handle, (takeOwnership ? freeHardwareBuffer : freeNoop)) {
- CHECK(mHandle != nullptr);
-}
-
-AHardwareBuffer* HardwareBufferHandle::get() const {
- return mHandle.get();
-}
-
GeneralResult<base::unique_fd> dupFd(int fd) {
if (fd < 0) {
return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "dupFd was passed an invalid fd";
@@ -269,42 +263,41 @@
}
GeneralResult<SharedMemory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
- return createSharedMemoryFromUniqueFd(NN_TRY(dupFd(fd)), size, prot, offset);
+ return createSharedMemoryFromUniqueFd(size, prot, NN_TRY(dupFd(fd)), offset);
}
GeneralResult<SharedMemory> createSharedMemoryFromAHWB(AHardwareBuffer* ahwb, bool takeOwnership) {
CHECK(ahwb != nullptr);
- auto handle = HardwareBufferHandle(ahwb, takeOwnership);
+ const Memory::HardwareBuffer::Deleter deleter = (takeOwnership ? freeHardwareBuffer : freeNoop);
+ Memory::HardwareBuffer handle = {.handle = Memory::HardwareBuffer::Handle(ahwb, deleter)};
+ return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
+}
- AHardwareBuffer_Desc bufferDesc;
- AHardwareBuffer_describe(ahwb, &bufferDesc);
+size_t getSize(const SharedMemory& memory) {
+ CHECK(memory != nullptr);
+ return std::visit([](const auto& x) { return getSize(x); }, memory->handle);
+}
- const bool isBlobMode = bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB;
- // memory size is not used for non-BLOB AHWB memory.
- const size_t size = isBlobMode ? bufferDesc.width : 0;
- std::string name = isBlobMode ? "hardware_buffer_blob" : "hardware_buffer";
+bool isAhwbBlob(const Memory::HardwareBuffer& memory) {
+ AHardwareBuffer* ahwb = memory.handle.get();
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(ahwb, &desc);
+ return desc.format == AHARDWAREBUFFER_FORMAT_BLOB;
+}
- return std::make_shared<const Memory>(Memory{
- .handle = std::move(handle),
- .size = size,
- .name = std::move(name),
- });
+bool isAhwbBlob(const SharedMemory& memory) {
+ CHECK(memory != nullptr);
+ if (!std::holds_alternative<Memory::HardwareBuffer>(memory->handle)) {
+ return false;
+ }
+ return isAhwbBlob(std::get<Memory::HardwareBuffer>(memory->handle));
}
GeneralResult<Mapping> map(const SharedMemory& memory) {
- if (memory->name == "mmap_fd") {
- return mapMemFd(*memory);
+ if (memory == nullptr) {
+ return NN_ERROR() << "Unable to map nullptr SharedMemory object";
}
- if (memory->name == "ashmem") {
- return mapAshmem(*memory);
- }
- if (memory->name == "hardware_buffer_blob") {
- return mapAhwbBlobMemory(std::get<HardwareBufferHandle>(memory->handle));
- }
- if (memory->name == "hardware_buffer") {
- return mapAhwbMemory(*memory);
- }
- return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map unknown memory " << memory->name;
+ return std::visit([](const auto& x) { return map(x); }, memory->handle);
}
bool flush(const Mapping& mapping) {
diff --git a/common/TypeUtils.cpp b/common/TypeUtils.cpp
index faa930f..56ee788 100644
--- a/common/TypeUtils.cpp
+++ b/common/TypeUtils.cpp
@@ -207,7 +207,7 @@
std::vector<size_t> poolSizes;
poolSizes.reserve(model.pools.size());
std::transform(model.pools.begin(), model.pools.end(), std::back_inserter(poolSizes),
- [](const SharedMemory& memory) { return memory->size; });
+ [](const SharedMemory& memory) { return getSize(memory); });
return std::make_pair(operandValuesSize, std::move(poolSizes));
}
@@ -721,20 +721,33 @@
return os << *handle;
}
-static std::ostream& operator<<(std::ostream& os, const HardwareBufferHandle& handle) {
- return os << (handle.get() != nullptr ? "<non-empty HardwareBufferHandle>"
- : "<empty HardwareBufferHandle>");
+static std::ostream& operator<<(std::ostream& os, const Memory::Ashmem& memory) {
+ return os << "Ashmem{.fd=" << (memory.fd.ok() ? "<valid fd>" : "<invalid fd>")
+ << ", .size=" << memory.size << "}";
}
-static std::ostream& operator<<(std::ostream& os,
- const std::variant<Handle, HardwareBufferHandle>& handle) {
- std::visit([&os](const auto& x) { os << x; }, handle);
- return os;
+static std::ostream& operator<<(std::ostream& os, const Memory::Fd& memory) {
+ return os << "Fd{.size=" << memory.size << ", .prot=" << memory.prot
+ << ", .fd=" << (memory.fd.ok() ? "<valid fd>" : "<invalid fd>")
+ << ", .offset=" << memory.offset << "}";
+}
+
+static std::ostream& operator<<(std::ostream& os, const Memory::HardwareBuffer& memory) {
+ if (memory.handle.get() == nullptr) {
+ return os << "<empty HardwareBuffer::Handle>";
+ }
+ return os << (isAhwbBlob(memory) ? "<AHardwareBuffer blob>" : "<non-blob AHardwareBuffer>");
+}
+
+static std::ostream& operator<<(std::ostream& os, const Memory::Unknown& memory) {
+ return os << "Unknown{.handle=" << memory.handle << ", .size=" << memory.size
+ << ", .name=" << memory.name << "}";
}
std::ostream& operator<<(std::ostream& os, const Memory& memory) {
- return os << "Memory{.handle=" << memory.handle << ", .size=" << memory.size
- << ", .name=" << memory.name << "}";
+ os << "Memory{.handle=";
+ std::visit([&os](const auto& x) { os << x; }, memory.handle);
+ return os << "}";
}
std::ostream& operator<<(std::ostream& os, const SharedMemory& memory) {
diff --git a/common/Validation.cpp b/common/Validation.cpp
index 99ed39d..91d894c 100644
--- a/common/Validation.cpp
+++ b/common/Validation.cpp
@@ -17,6 +17,7 @@
#include "Validation.h"
#include <android-base/logging.h>
+#include <android-base/mapped_file.h>
#include <algorithm>
#include <cctype>
@@ -738,43 +739,37 @@
return validateHandle(*handle);
}
+Result<Version> validateMemory(const Memory::Ashmem& memory) {
+ NN_VALIDATE(memory.fd.ok());
+ NN_VALIDATE_NE(memory.size, 0u);
+ return Version::ANDROID_OC_MR1;
+}
+
+Result<Version> validateMemory(const Memory::Fd& memory) {
+ NN_VALIDATE(memory.fd.ok());
+ NN_VALIDATE_NE(memory.size, 0u);
+
+ // `prot` is allowed to be either PROT_NONE (which has a value of 0) or the bitwise OR of either
+ // PROT_READ or PROT_WRITE. If any other bits are set, the `prot` field is invalid.
+ constexpr int kAllowedBits = PROT_READ | PROT_WRITE;
+ NN_VALIDATE_EQ(memory.prot & ~kAllowedBits, 0);
+
+ return Version::ANDROID_OC_MR1;
+}
+
+Result<Version> validateMemory(const Memory::HardwareBuffer& memory) {
+ NN_VALIDATE(memory.handle.get() != nullptr);
+ return Version::ANDROID_Q;
+}
+
+Result<Version> validateMemory(const Memory::Unknown& memory) {
+ NN_TRY(validateHandle(memory.handle));
+ return Version::ANDROID_Q;
+}
+
Result<Version> validateSharedMemory(const SharedMemory& memory) {
NN_VALIDATE(memory != nullptr);
-
- if (memory->name == "ashmem") {
- NN_VALIDATE_NE(memory->size, 0u);
- NN_VALIDATE(std::holds_alternative<Handle>(memory->handle));
- NN_TRY(validateHandle(std::get<Handle>(memory->handle)));
- return Version::ANDROID_OC_MR1;
- }
- if (memory->name == "mmap_fd") {
- NN_VALIDATE_NE(memory->size, 0u);
- NN_VALIDATE(std::holds_alternative<Handle>(memory->handle));
- NN_TRY(validateHandle(std::get<Handle>(memory->handle)));
- return Version::ANDROID_OC_MR1;
- }
- if (memory->name == "hardware_buffer_blob") {
- NN_VALIDATE_NE(memory->size, 0u);
- NN_VALIDATE(std::holds_alternative<HardwareBufferHandle>(memory->handle));
- NN_VALIDATE(std::get<HardwareBufferHandle>(memory->handle).get() != nullptr);
- return Version::ANDROID_Q;
- }
- if (memory->name == "hardware_buffer") {
- // For hardware_buffer memory, all size information is bound to the AHardwareBuffer, so
- // memory.size must be 0.
- NN_VALIDATE_EQ(memory->size, 0u);
- // hardware_buffer can be represented by either Handle or HardwareBufferHandle.
- if (const auto* handle = std::get_if<Handle>(&memory->handle)) {
- NN_TRY(validateHandle(*handle));
- return Version::ANDROID_Q;
- }
- if (const auto* handle = std::get_if<HardwareBufferHandle>(&memory->handle)) {
- NN_VALIDATE(handle->get() != nullptr);
- return Version::ANDROID_Q;
- }
- }
-
- NN_VALIDATE_FAIL() << "Unknown memory type " << memory->name;
+ return std::visit([](const auto& x) { return validateMemory(x); }, memory->handle);
}
Result<void> validateModelSubgraphInputOutputs(const std::vector<uint32_t>& indexes,
@@ -1114,7 +1109,7 @@
std::transform(request.pools.begin(), request.pools.end(), std::back_inserter(memorySizes),
[](const Request::MemoryPool& memoryPool) {
const auto* memory = std::get_if<SharedMemory>(&memoryPool);
- return memory != nullptr ? (*memory)->size : 0;
+ return memory != nullptr ? getSize(*memory) : 0;
});
for (size_t i = 0; i < request.inputs.size(); ++i) {
diff --git a/common/include/nnapi/SharedMemory.h b/common/include/nnapi/SharedMemory.h
index c0873f6..a102feb 100644
--- a/common/include/nnapi/SharedMemory.h
+++ b/common/include/nnapi/SharedMemory.h
@@ -29,30 +29,8 @@
#include "nnapi/Result.h"
#include "nnapi/Types.h"
-// Forward declare AHardwareBuffer
-extern "C" typedef struct AHardwareBuffer AHardwareBuffer;
-
namespace android::nn {
-// RAII wrapper for AHardwareBuffer
-class HardwareBufferHandle {
- public:
- // Precondition: handle != nullptr
- HardwareBufferHandle(AHardwareBuffer* handle, bool takeOwnership);
-
- AHardwareBuffer* get() const;
-
- private:
- using Deleter = std::add_pointer_t<void(AHardwareBuffer*)>;
- std::unique_ptr<AHardwareBuffer, Deleter> mHandle;
-};
-
-struct Memory {
- std::variant<Handle, HardwareBufferHandle> handle;
- size_t size = 0;
- std::string name;
-};
-
class MutableMemoryBuilder {
public:
explicit MutableMemoryBuilder(uint32_t poolIndex);
@@ -111,8 +89,16 @@
// Precondition: ahwb != nullptr
GeneralResult<SharedMemory> createSharedMemoryFromAHWB(AHardwareBuffer* ahwb, bool takeOwnership);
+// Precondition: memory != nullptr
+size_t getSize(const SharedMemory& memory);
+
+bool isAhwbBlob(const Memory::HardwareBuffer& memory);
+
+// Precondition: memory != nullptr
+bool isAhwbBlob(const SharedMemory& memory);
+
struct Mapping {
- std::variant<void*, const void*> pointer;
+ std::variant<const void*, void*> pointer;
size_t size;
std::any context;
};
diff --git a/common/include/nnapi/Types.h b/common/include/nnapi/Types.h
index dc8dd91..e182c95 100644
--- a/common/include/nnapi/Types.h
+++ b/common/include/nnapi/Types.h
@@ -35,6 +35,9 @@
#include "nnapi/OperationTypes.h"
#include "nnapi/Result.h"
+// Forward declare AHardwareBuffer
+extern "C" typedef struct AHardwareBuffer AHardwareBuffer;
+
namespace android::nn {
// Forward declarations
@@ -250,6 +253,35 @@
using SharedHandle = std::shared_ptr<const Handle>;
+struct Memory {
+ struct Ashmem {
+ base::unique_fd fd;
+ size_t size;
+ };
+
+ struct Fd {
+ size_t size;
+ int prot;
+ base::unique_fd fd;
+ size_t offset;
+ };
+
+ // RAII wrapper for AHardwareBuffer
+ struct HardwareBuffer {
+ using Deleter = std::add_pointer_t<void(AHardwareBuffer*)>;
+ using Handle = std::unique_ptr<AHardwareBuffer, Deleter>;
+ Handle handle;
+ };
+
+ struct Unknown {
+ Handle handle;
+ size_t size;
+ std::string name;
+ };
+
+ std::variant<Ashmem, Fd, HardwareBuffer, Unknown> handle;
+};
+
struct Model {
struct Subgraph {
std::vector<Operand> operands;
diff --git a/driver/sample_aidl/Android.bp b/driver/sample_aidl/Android.bp
index a058d28..21b9905 100644
--- a/driver/sample_aidl/Android.bp
+++ b/driver/sample_aidl/Android.bp
@@ -54,6 +54,7 @@
"libutils",
],
static_libs: [
+ "libaidlcommonsupport",
"libneuralnetworks_common",
"neuralnetworks_utils_hal_aidl",
"neuralnetworks_utils_hal_common",
diff --git a/driver/sample_shim/Android.bp b/driver/sample_shim/Android.bp
index 012cb46..7508625 100644
--- a/driver/sample_shim/Android.bp
+++ b/driver/sample_shim/Android.bp
@@ -57,7 +57,9 @@
],
static_libs: [
"android.hardware.common-V2-ndk_platform",
+ "android.hardware.graphics.common-V2-ndk_platform",
"android.hardware.neuralnetworks-V1-ndk_platform",
+ "libaidlcommonsupport",
"libarect",
"libcutils",
"libneuralnetworks_shim_static",
@@ -105,6 +107,7 @@
"libneuralnetworks_headers",
],
static_libs: [
+ "libaidlcommonsupport",
"libarect",
"libneuralnetworks_common",
"neuralnetworks_utils_hal_aidl",
diff --git a/driver/sample_shim/android_arm/neuralnetworks_sample_sl_driver_prebuilt.so b/driver/sample_shim/android_arm/neuralnetworks_sample_sl_driver_prebuilt.so
index db41329..0bd47d4 100755
--- a/driver/sample_shim/android_arm/neuralnetworks_sample_sl_driver_prebuilt.so
+++ b/driver/sample_shim/android_arm/neuralnetworks_sample_sl_driver_prebuilt.so
Binary files differ
diff --git a/driver/sample_shim/android_arm64/neuralnetworks_sample_sl_driver_prebuilt.so b/driver/sample_shim/android_arm64/neuralnetworks_sample_sl_driver_prebuilt.so
index 2ff4e2d..76a25ac 100755
--- a/driver/sample_shim/android_arm64/neuralnetworks_sample_sl_driver_prebuilt.so
+++ b/driver/sample_shim/android_arm64/neuralnetworks_sample_sl_driver_prebuilt.so
Binary files differ
diff --git a/driver/sample_shim/android_x86/neuralnetworks_sample_sl_driver_prebuilt.so b/driver/sample_shim/android_x86/neuralnetworks_sample_sl_driver_prebuilt.so
index 5fa0b6a..3201068 100755
--- a/driver/sample_shim/android_x86/neuralnetworks_sample_sl_driver_prebuilt.so
+++ b/driver/sample_shim/android_x86/neuralnetworks_sample_sl_driver_prebuilt.so
Binary files differ
diff --git a/driver/sample_shim/android_x86_64/neuralnetworks_sample_sl_driver_prebuilt.so b/driver/sample_shim/android_x86_64/neuralnetworks_sample_sl_driver_prebuilt.so
index 397eef6..f5ec6e2 100755
--- a/driver/sample_shim/android_x86_64/neuralnetworks_sample_sl_driver_prebuilt.so
+++ b/driver/sample_shim/android_x86_64/neuralnetworks_sample_sl_driver_prebuilt.so
Binary files differ
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 107124a..704aa99 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -115,6 +115,7 @@
// this would remove half of dependencies here.
static_libs: [
"android.hardware.common-V2-ndk_platform",
+ "android.hardware.graphics.common-V2-ndk_platform",
"android.hardware.neuralnetworks-V1-ndk_platform",
"[email protected]",
"[email protected]",
@@ -122,6 +123,7 @@
"[email protected]",
"[email protected]",
"[email protected]",
+ "libaidlcommonsupport",
"libbase",
"libcrypto_static",
"libcutils",
diff --git a/runtime/ExecutionBuilder.cpp b/runtime/ExecutionBuilder.cpp
index 3985d12..53aa464 100644
--- a/runtime/ExecutionBuilder.cpp
+++ b/runtime/ExecutionBuilder.cpp
@@ -226,7 +226,7 @@
// length. For other memories that do not allow this semantic, it is checked in
// MemoryValidatorBase::validate before reaching here.
if (validate(memory->getMemory()).ok() && offset == 0 && length == 0) {
- length = memory->getMemory()->size;
+ length = memory->getSize();
}
// TODO validate the rest
uint32_t poolIndex = mMemories.add(memory);
@@ -307,7 +307,7 @@
// length. For other memories that do not allow this semantic, it is checked in
// MemoryValidatorBase::validate before reaching here.
if (validate(memory->getMemory()).ok() && offset == 0 && length == 0) {
- length = memory->getMemory()->size;
+ length = memory->getSize();
}
// TODO validate the rest
uint32_t poolIndex = mMemories.add(memory);
diff --git a/runtime/Memory.cpp b/runtime/Memory.cpp
index 7b46cf1..33f3ab7 100644
--- a/runtime/Memory.cpp
+++ b/runtime/Memory.cpp
@@ -182,7 +182,7 @@
RuntimeMemory::RuntimeMemory(SharedMemory memory) : kMemory(std::move(memory)) {
CHECK(kMemory != nullptr);
- mValidator = std::make_unique<SizedMemoryValidator>(kMemory->size);
+ mValidator = std::make_unique<SizedMemoryValidator>(nn::getSize(kMemory));
}
RuntimeMemory::RuntimeMemory(SharedMemory memory, std::unique_ptr<MemoryValidatorBase> validator)
@@ -548,8 +548,8 @@
}
std::unique_ptr<MemoryValidatorBase> validator;
- if (memory.value()->name == "hardware_buffer_blob") {
- validator = std::make_unique<SizedMemoryValidator>(memory.value()->size);
+ if (isAhwbBlob(memory.value())) {
+ validator = std::make_unique<SizedMemoryValidator>(nn::getSize(memory.value()));
} else {
validator = std::make_unique<AHardwareBufferNonBlobValidator>();
}
diff --git a/runtime/Memory.h b/runtime/Memory.h
index 3ce32f5..850f70a 100644
--- a/runtime/Memory.h
+++ b/runtime/Memory.h
@@ -175,7 +175,7 @@
Request::MemoryPool getMemoryPool() const;
const SharedMemory& getMemory() const { return kMemory; }
const SharedBuffer& getIBuffer() const { return kBuffer; }
- virtual uint32_t getSize() const { return getMemory()->size; }
+ virtual uint32_t getSize() const { return nn::getSize(getMemory()); }
virtual std::optional<RunTimePoolInfo> getRunTimePoolInfo() const;
MemoryValidatorBase& getValidator() const {
@@ -278,7 +278,7 @@
uint8_t* getPointer() const;
std::optional<RunTimePoolInfo> getRunTimePoolInfo() const override {
- return RunTimePoolInfo::createFromExistingBuffer(getPointer(), kMemory->size);
+ return RunTimePoolInfo::createFromExistingBuffer(getPointer(), nn::getSize(kMemory));
}
// prefer using MemoryAshmem::create
@@ -332,7 +332,7 @@
uint8_t* getPointer() const;
std::optional<RunTimePoolInfo> getRunTimePoolInfo() const override {
- return RunTimePoolInfo::createFromExistingBuffer(getPointer(), kMemory->size);
+ return RunTimePoolInfo::createFromExistingBuffer(getPointer(), nn::getSize(kMemory));
}
// prefer using MemoryRuntimeAHWB::create
diff --git a/runtime/test/Android.bp b/runtime/test/Android.bp
index f25485f..66196c3 100644
--- a/runtime/test/Android.bp
+++ b/runtime/test/Android.bp
@@ -49,11 +49,13 @@
],
static_libs: [
"android.hardware.common-V2-ndk_platform",
+ "android.hardware.graphics.common-V2-ndk_platform",
"android.hardware.neuralnetworks-V1-ndk_platform",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
+ "libaidlcommonsupport",
"libc++fs",
"libneuralnetworks_generated_test_harness",
"libtextclassifier_hash_static",
diff --git a/runtime/test/TestMemoryDomain.cpp b/runtime/test/TestMemoryDomain.cpp
index 1d11b44..0f462bc 100644
--- a/runtime/test/TestMemoryDomain.cpp
+++ b/runtime/test/TestMemoryDomain.cpp
@@ -39,6 +39,7 @@
using WrapperResult = test_wrapper::Result;
using Type = test_wrapper::Type;
using android::sp;
+using android::nn::isAhwbBlob;
namespace {
@@ -281,6 +282,10 @@
const AllocateReturn kAllocateReturn = std::get<2>(GetParam());
};
+bool isAshmem(const SharedMemory& memory) {
+ return memory != nullptr && std::holds_alternative<Memory::Ashmem>(memory->handle);
+}
+
// Test device memory allocation on a compilation with only a single partition.
TEST_P(MemoryDomainTest, SinglePartition) {
createAndRegisterDriver(
@@ -310,9 +315,9 @@
const auto& memory = m->getMemory();
EXPECT_TRUE(validate(memory).ok());
if (kUseV1_2Driver) {
- EXPECT_EQ(memory->name, "ashmem");
+ EXPECT_TRUE(isAshmem(memory));
} else {
- EXPECT_EQ(memory->name, "hardware_buffer_blob");
+ EXPECT_TRUE(isAhwbBlob(memory));
}
}
}
@@ -348,9 +353,9 @@
const auto& memory = m->getMemory();
EXPECT_TRUE(validate(memory).ok());
if (kUseV1_2Driver) {
- EXPECT_EQ(memory->name, "ashmem");
+ EXPECT_TRUE(isAshmem(memory));
} else {
- EXPECT_EQ(memory->name, "hardware_buffer_blob");
+ EXPECT_TRUE(isAhwbBlob(memory));
}
}
}
@@ -372,9 +377,9 @@
const auto& memory = m->getMemory();
EXPECT_TRUE(validate(memory).ok());
if (kUseV1_2Driver) {
- EXPECT_EQ(memory->name, "ashmem");
+ EXPECT_TRUE(isAshmem(memory));
} else {
- EXPECT_EQ(memory->name, "hardware_buffer_blob");
+ EXPECT_TRUE(isAhwbBlob(memory));
}
}
}
@@ -395,9 +400,9 @@
const auto& memory = m->getMemory();
EXPECT_TRUE(validate(memory).ok());
if (kUseV1_2Driver) {
- EXPECT_EQ(memory->name, "ashmem");
+ EXPECT_TRUE(isAshmem(memory));
} else {
- EXPECT_EQ(memory->name, "hardware_buffer_blob");
+ EXPECT_TRUE(isAhwbBlob(memory));
}
}
}
diff --git a/shim_and_sl/Android.bp b/shim_and_sl/Android.bp
index 5f1392a..913e8f8 100644
--- a/shim_and_sl/Android.bp
+++ b/shim_and_sl/Android.bp
@@ -71,6 +71,7 @@
static_libs: [
"android.hardware.common-V2-ndk_platform",
"android.hardware.neuralnetworks-V1-ndk_platform",
+ "libaidlcommonsupport",
"libarect",
"libcutils",
"neuralnetworks_supportlibrary_loader",
@@ -114,7 +115,9 @@
},
static_libs: [
"android.hardware.common-V2-ndk_platform",
+ "android.hardware.graphics.common-V2-ndk_platform",
"android.hardware.neuralnetworks-V1-ndk_platform",
+ "libaidlcommonsupport",
"libcutils",
"neuralnetworks_supportlibrary_loader",
"neuralnetworks_types",
diff --git a/shim_and_sl/ShimConverter.cpp b/shim_and_sl/ShimConverter.cpp
index b028c49..6282dc9 100644
--- a/shim_and_sl/ShimConverter.cpp
+++ b/shim_and_sl/ShimConverter.cpp
@@ -17,10 +17,16 @@
#define LOG_TAG "ShimConverter"
#include "ShimConverter.h"
+
+#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
+#include <android-base/mapped_file.h>
#include <android-base/scopeguard.h>
#include <android/hardware_buffer.h>
#include <cutils/native_handle.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/hal/aidl/Conversions.h>
+#include <nnapi/hal/aidl/Utils.h>
#include <sys/mman.h>
#include <vndk/hardware_buffer.h>
@@ -29,10 +35,6 @@
#include <utility>
#include <vector>
-#include <nnapi/TypeUtils.h>
-#include <nnapi/hal/aidl/Conversions.h>
-#include <nnapi/hal/aidl/Utils.h>
-
using namespace ::android::nn::sl_wrapper;
namespace aidl::android::hardware::neuralnetworks {
@@ -341,106 +343,86 @@
return ShimConvertedModel{.memory = std::move(memoryPools), .models = std::move(result)};
}
-namespace {
-
-uint32_t roundUpToMultiple(uint32_t value, uint32_t multiple) {
- return (value + multiple - 1) / multiple * multiple;
-}
-
-} // namespace
-
std::unique_ptr<::android::nn::sl_wrapper::Memory> convertFromHAL(
const NnApiSupportLibrary* nnapi, const neuralnetworks::Memory& pool) {
- if (pool.name == "ashmem") {
- size_t size = pool.size;
- int fd = pool.handle.fds[0].get();
+ using Tag = neuralnetworks::Memory::Tag;
+ switch (pool.getTag()) {
+ case Tag::ashmem: {
+ const auto& ashmem = pool.get<Tag::ashmem>();
+ size_t size = ashmem.size;
+ int fd = ashmem.fd.get();
- auto memory = std::make_unique<::android::nn::sl_wrapper::Memory>(
- nnapi, size, PROT_READ | PROT_WRITE, fd, 0, /*ownsFd=*/false);
- if (!memory->isValid()) {
- return nullptr;
+ auto memory = std::make_unique<::android::nn::sl_wrapper::Memory>(
+ nnapi, size, PROT_READ | PROT_WRITE, fd, 0, /*ownsFd=*/false);
+ if (!memory->isValid()) {
+ return nullptr;
+ }
+ return memory;
}
- return memory;
- } else if (pool.name == "mmap_fd") {
- size_t size = pool.size;
- int fd = pool.handle.fds[0].get();
- int prot = pool.handle.ints[0];
- size_t offset = ::android::nn::getOffsetFromInts(pool.handle.ints[1], pool.handle.ints[2]);
+ case Tag::mappableFile: {
+ const auto& mappableFile = pool.get<Tag::mappableFile>();
+ size_t size = mappableFile.length;
+ int fd = mappableFile.fd.get();
+ int prot = mappableFile.prot & (PROT_READ | PROT_WRITE);
+ size_t offset = mappableFile.offset;
- auto memory = std::make_unique<::android::nn::sl_wrapper::Memory>(nnapi, size, prot, fd,
- offset, /*ownsFd=*/false);
-
- if (!memory->isValid()) {
- return nullptr;
+ auto memory = std::make_unique<::android::nn::sl_wrapper::Memory>(
+ nnapi, size, prot, fd, offset, /*ownsFd=*/false);
+ if (!memory->isValid()) {
+ return nullptr;
+ }
+ return memory;
}
- return memory;
- } else if (pool.name == "hardware_buffer_blob") {
- const auto numFds = pool.handle.fds.size();
- const auto numInts = pool.handle.ints.size();
- auto handle = native_handle_create(numFds, numInts);
- const auto handleGuard = ::android::base::make_scope_guard([handle] {
- native_handle_close(handle);
- native_handle_delete(handle);
- });
+ case Tag::hardwareBuffer: {
+ const auto& hardwareBuffer = pool.get<Tag::hardwareBuffer>();
- std::fill(handle->data + 0, handle->data + handle->numFds, -1);
- for (int i = 0; i < numFds; ++i) {
- int fd = dup(pool.handle.fds[i].get());
- if (fd == -1) {
+ native_handle_t* handle = ::android::dupFromAidl(hardwareBuffer.handle);
+ if (handle == nullptr) {
LOG(ERROR) << "Dup of the hardware_buffer_blob memory pool failed";
return nullptr;
}
- handle->data[i] = fd;
- }
- for (int i = 0; i < numInts; ++i) {
- handle->data[numFds + i] = pool.handle.ints[i];
- }
- const auto size = pool.size;
- const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
- const auto usage =
- AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- const uint32_t width = size;
- const uint32_t height = 1; // height is always 1 for BLOB mode AHardwareBuffer.
- const uint32_t layers = 1; // layers is always 1 for BLOB mode AHardwareBuffer.
-
- AHardwareBuffer* hardwareBuffer = nullptr;
-
- // AHardwareBuffer_createFromHandle() might fail because an allocator
- // expects a specific stride value. In that case, we try to guess it by
- // aligning the width to small powers of 2.
- ::android::status_t status = ::android::UNKNOWN_ERROR;
- for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
- const uint32_t stride = roundUpToMultiple(width, alignment);
- AHardwareBuffer_Desc desc{
- .width = width,
- .height = height,
- .layers = layers,
- .format = format,
- .usage = usage,
- .stride = stride,
- };
- status = AHardwareBuffer_createFromHandle(
- &desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
- &hardwareBuffer);
- if (status == ::android::NO_ERROR) {
- break;
+ const auto handleGuard = ::android::base::make_scope_guard([handle] {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ });
+ for (size_t i = 0; i < handle->numFds; ++i) {
+ if (handle->data[i] == -1) {
+ LOG(ERROR) << "Dup of the hardware_buffer_blob memory pool failed";
+ return nullptr;
+ }
}
+
+ const AHardwareBuffer_Desc desc{
+ .width = static_cast<uint32_t>(hardwareBuffer.description.width),
+ .height = static_cast<uint32_t>(hardwareBuffer.description.height),
+ .layers = static_cast<uint32_t>(hardwareBuffer.description.layers),
+ .format = static_cast<uint32_t>(hardwareBuffer.description.format),
+ .usage = static_cast<uint64_t>(hardwareBuffer.description.usage),
+ .stride = static_cast<uint32_t>(hardwareBuffer.description.stride),
+ };
+ AHardwareBuffer* ahwb = nullptr;
+ const ::android::status_t status = AHardwareBuffer_createFromHandle(
+ &desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &ahwb);
+ if (status != ::android::NO_ERROR) {
+ LOG(ERROR) << "createFromHandle failed";
+ return nullptr;
+ }
+
+ const bool isBlob = desc.format == AHARDWAREBUFFER_FORMAT_BLOB;
+ const size_t size = isBlob ? desc.width : 0;
+
+ // Takes ownership of hardwareBuffer, handle gets closed
+ auto memory =
+ std::make_unique<::android::nn::sl_wrapper::Memory>(nnapi, ahwb,
+ /*ownAHB=*/true, size);
+ if (!memory->isValid()) {
+ return nullptr;
+ }
+ return memory;
}
-
- if (status != ::android::NO_ERROR) {
- LOG(ERROR) << "createFromHandle failed";
- return nullptr;
- }
-
- // Takes ownership of hardwareBuffer, handle gets closed
- auto memory = std::make_unique<::android::nn::sl_wrapper::Memory>(nnapi, hardwareBuffer,
- /*ownAHB=*/true, size);
-
- return memory;
- } else {
- LOG(ERROR) << "Can't convert to SL Memory, unknown pool name: " << pool.name;
- return nullptr;
}
+ LOG(ERROR) << "Can't convert to SL Memory, unknown pool tag: " << pool.getTag();
+ return nullptr;
}
} // namespace aidl::android::hardware::neuralnetworks