1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Utils.h"
18 
19 #include <aidl/android/hardware/common/Ashmem.h>
20 #include <aidl/android/hardware/common/MappableFile.h>
21 #include <aidl/android/hardware/graphics/common/HardwareBuffer.h>
22 #include <android/binder_auto_utils.h>
23 #include <android/binder_status.h>
24 #include <nnapi/Result.h>
25 #include <nnapi/SharedMemory.h>
26 
27 namespace aidl::android::hardware::neuralnetworks::utils {
28 namespace {
29 
30 nn::GeneralResult<ndk::ScopedFileDescriptor> clone(const ndk::ScopedFileDescriptor& fd);
31 using utils::clone;
32 
33 template <typename Type>
cloneVec(const std::vector<Type> & arguments)34 nn::GeneralResult<std::vector<Type>> cloneVec(const std::vector<Type>& arguments) {
35     std::vector<Type> clonedObjects;
36     clonedObjects.reserve(arguments.size());
37     for (const auto& argument : arguments) {
38         clonedObjects.push_back(NN_TRY(clone(argument)));
39     }
40     return clonedObjects;
41 }
42 
43 template <typename Type>
clone(const std::vector<Type> & arguments)44 nn::GeneralResult<std::vector<Type>> clone(const std::vector<Type>& arguments) {
45     return cloneVec(arguments);
46 }
47 
clone(const ndk::ScopedFileDescriptor & fd)48 nn::GeneralResult<ndk::ScopedFileDescriptor> clone(const ndk::ScopedFileDescriptor& fd) {
49     auto duplicatedFd = NN_TRY(nn::dupFd(fd.get()));
50     return ndk::ScopedFileDescriptor(duplicatedFd.release());
51 }
52 
clone(const common::NativeHandle & handle)53 nn::GeneralResult<common::NativeHandle> clone(const common::NativeHandle& handle) {
54     auto fds = NN_TRY(cloneVec(handle.fds));
55     return common::NativeHandle{
56             .fds = std::move(fds),
57             .ints = handle.ints,
58     };
59 }
60 
61 }  // namespace
62 
clone(const Memory & memory)63 nn::GeneralResult<Memory> clone(const Memory& memory) {
64     switch (memory.getTag()) {
65         case Memory::Tag::ashmem: {
66             const auto& ashmem = memory.get<Memory::Tag::ashmem>();
67             auto fd = NN_TRY(clone(ashmem.fd));
68             auto handle = common::Ashmem{
69                     .fd = std::move(fd),
70                     .size = ashmem.size,
71             };
72             return Memory::make<Memory::Tag::ashmem>(std::move(handle));
73         }
74         case Memory::Tag::mappableFile: {
75             const auto& memFd = memory.get<Memory::Tag::mappableFile>();
76             auto fd = NN_TRY(clone(memFd.fd));
77             auto handle = common::MappableFile{
78                     .length = memFd.length,
79                     .prot = memFd.prot,
80                     .fd = std::move(fd),
81                     .offset = memFd.offset,
82             };
83             return Memory::make<Memory::Tag::mappableFile>(std::move(handle));
84         }
85         case Memory::Tag::hardwareBuffer: {
86             const auto& hardwareBuffer = memory.get<Memory::Tag::hardwareBuffer>();
87             auto handle = NN_TRY(clone(hardwareBuffer.handle));
88             auto ahwbHandle = graphics::common::HardwareBuffer{
89                     .description = hardwareBuffer.description,
90                     .handle = std::move(handle),
91             };
92             return Memory::make<Memory::Tag::hardwareBuffer>(std::move(ahwbHandle));
93         }
94     }
95     return (NN_ERROR() << "Unrecognized Memory::Tag: " << underlyingType(memory.getTag()))
96             .
97             operator nn::GeneralResult<Memory>();
98 }
99 
clone(const RequestMemoryPool & requestPool)100 nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool) {
101     using Tag = RequestMemoryPool::Tag;
102     switch (requestPool.getTag()) {
103         case Tag::pool:
104             return RequestMemoryPool::make<Tag::pool>(NN_TRY(clone(requestPool.get<Tag::pool>())));
105         case Tag::token:
106             return RequestMemoryPool::make<Tag::token>(requestPool.get<Tag::token>());
107     }
108     // Using explicit type conversion because std::variant inside the RequestMemoryPool confuses the
109     // compiler.
110     return (NN_ERROR() << "Unrecognized request pool tag: " << underlyingType(requestPool.getTag()))
111             .
112             operator nn::GeneralResult<RequestMemoryPool>();
113 }
114 
clone(const Request & request)115 nn::GeneralResult<Request> clone(const Request& request) {
116     auto pools = NN_TRY(clone(request.pools));
117     return Request{
118             .inputs = request.inputs,
119             .outputs = request.outputs,
120             .pools = std::move(pools),
121     };
122 }
123 
clone(const Model & model)124 nn::GeneralResult<Model> clone(const Model& model) {
125     auto pools = NN_TRY(clone(model.pools));
126     return Model{
127             .main = model.main,
128             .referenced = model.referenced,
129             .operandValues = model.operandValues,
130             .pools = std::move(pools),
131             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
132             .extensionNameToPrefix = model.extensionNameToPrefix,
133     };
134 }
135 
handleTransportError(const ndk::ScopedAStatus & ret)136 nn::GeneralResult<void> handleTransportError(const ndk::ScopedAStatus& ret) {
137     if (ret.getStatus() == STATUS_DEAD_OBJECT) {
138         return nn::error(nn::ErrorStatus::DEAD_OBJECT)
139                << "Binder transaction returned STATUS_DEAD_OBJECT: " << ret.getDescription();
140     }
141     if (ret.isOk()) {
142         return {};
143     }
144     if (ret.getExceptionCode() != EX_SERVICE_SPECIFIC) {
145         return nn::error(nn::ErrorStatus::GENERAL_FAILURE)
146                << "Binder transaction returned exception: " << ret.getDescription();
147     }
148     return nn::error(static_cast<nn::ErrorStatus>(ret.getServiceSpecificError()))
149            << ret.getMessage();
150 }
151 
152 }  // namespace aidl::android::hardware::neuralnetworks::utils
153