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 #define LOG_TAG "ShimConverter"
18 
19 #include "ShimConverter.h"
20 
21 #include <aidlcommonsupport/NativeHandle.h>
22 #include <android-base/logging.h>
23 #include <android-base/mapped_file.h>
24 #include <android-base/scopeguard.h>
25 #include <android/hardware_buffer.h>
26 #include <cutils/native_handle.h>
27 #include <nnapi/TypeUtils.h>
28 #include <nnapi/hal/aidl/Conversions.h>
29 #include <nnapi/hal/aidl/Utils.h>
30 #include <sys/mman.h>
31 #include <vndk/hardware_buffer.h>
32 
33 #include <algorithm>
34 #include <memory>
35 #include <string>
36 #include <utility>
37 #include <vector>
38 
39 using namespace ::android::nn::sl_wrapper;
40 
41 namespace aidl::android::hardware::neuralnetworks {
42 
43 namespace {
44 
45 // Assumes that isValid(model) holds
convertSubgraphFromHAL(const NnApiSupportLibrary * nnapi,const std::vector<std::unique_ptr<::android::nn::sl_wrapper::Memory>> & memoryPools,const neuralnetworks::Model & model,std::vector<std::optional<::android::nn::sl_wrapper::Model>> * allModels,size_t subgraphIndex,const std::vector<uint8_t> & copiedOperandValues,ErrorStatus * errorStatus)46 ANeuralNetworksModel* convertSubgraphFromHAL(
47         const NnApiSupportLibrary* nnapi,
48         const std::vector<std::unique_ptr<::android::nn::sl_wrapper::Memory>>& memoryPools,
49         const neuralnetworks::Model& model,
50         std::vector<std::optional<::android::nn::sl_wrapper::Model>>* allModels,
51         size_t subgraphIndex, const std::vector<uint8_t>& copiedOperandValues,
52         ErrorStatus* errorStatus) {
53     *errorStatus = ErrorStatus::NONE;
54     if ((*allModels)[subgraphIndex].has_value()) {
55         return (*allModels)[subgraphIndex]->getHandle();
56     }
57 
58     const auto& subgraph = subgraphIndex == 0 ? model.main : model.referenced[subgraphIndex - 1];
59     ::android::nn::sl_wrapper::Model resultModel(nnapi);
60 
61     resultModel.relaxComputationFloat32toFloat16(model.relaxComputationFloat32toFloat16);
62 
63     auto getExtensionName = [&](uint16_t prefix) -> const std::string* {
64         for (const auto& nameToPrefix : model.extensionNameToPrefix) {
65             if (prefix == nameToPrefix.prefix) {
66                 return &nameToPrefix.name;
67             }
68         }
69         return nullptr;
70     };
71 
72     for (int i = 0; i < subgraph.operands.size(); ++i) {
73         const auto& operand = subgraph.operands[i];
74 
75         const std::vector<uint32_t> dimensions =
76                 ::android::nn::toUnsigned(operand.dimensions).value();
77 
78         ::android::nn::wrapper::OperandType operandType(
79                 static_cast<::android::nn::wrapper::Type>(operand.type), dimensions, operand.scale,
80                 operand.zeroPoint);
81 
82         if (operand.type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
83             const auto& params = operand.extraParams->get<OperandExtraParams::Tag::channelQuant>();
84             operandType.channelQuant = ::android::nn::wrapper::SymmPerChannelQuantParams(
85                     params.scales, static_cast<uint32_t>(params.channelDim));
86         }
87 
88         if (::android::nn::isExtension(static_cast<::android::nn::OperandType>(operand.type))) {
89             uint16_t extensionPrefix =
90                     ::android::nn::getExtensionPrefix(static_cast<uint32_t>(operand.type));
91             uint16_t typeWithinExtension =
92                     ::android::nn::getTypeWithinExtension(static_cast<uint32_t>(operand.type));
93 
94             auto* extensionName = getExtensionName(extensionPrefix);
95             if (extensionName == nullptr) {
96                 LOG(ERROR) << "Unknown extension prefix " << extensionPrefix;
97                 *errorStatus = ErrorStatus::INVALID_ARGUMENT;
98                 return nullptr;
99             }
100             resultModel.getExtensionOperandType(*extensionName, typeWithinExtension,
101                                                 &operandType.operandType.type);
102             if (!resultModel.isValid()) {
103                 LOG(ERROR) << "Failed to get extension operand with index " << i;
104                 *errorStatus = ErrorStatus::INVALID_ARGUMENT;
105                 return nullptr;
106             }
107         }
108 
109         uint32_t operandIndex = resultModel.addOperand(&operandType);
110         if (!resultModel.isValid()) {
111             LOG(ERROR) << "Failed to add operand with index " << i;
112             *errorStatus = ErrorStatus::INVALID_ARGUMENT;
113             return nullptr;
114         }
115 
116         if (operand.extraParams &&
117             operand.extraParams->getTag() == OperandExtraParams::Tag::extension) {
118             const auto& extensionData =
119                     operand.extraParams->get<OperandExtraParams::Tag::extension>();
120             resultModel.setOperandExtensionData(operandIndex, extensionData.data(),
121                                                 extensionData.size());
122             if (!resultModel.isValid()) {
123                 LOG(ERROR) << "Failed to add extension data for operand with index " << i;
124                 *errorStatus = ErrorStatus::INVALID_ARGUMENT;
125                 return nullptr;
126             }
127         }
128 
129         switch (operand.lifetime) {
130             case OperandLifeTime::CONSTANT_COPY: {
131                 if (operand.location.length <=
132                     ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES) {
133                     resultModel.setOperandValue(
134                             i, model.operandValues.data() + operand.location.offset,
135                             operand.location.length);
136                 } else {
137                     // If length is larger than 128 bytes, we are responsible for making sure
138                     // that value outlives the model. If this case exists, then we created
139                     // an internal copy, that is used here:
140                     resultModel.setOperandValue(
141                             i, copiedOperandValues.data() + operand.location.offset,
142                             operand.location.length);
143                 }
144                 break;
145             }
146             case OperandLifeTime::CONSTANT_POOL: {
147                 resultModel.setOperandValueFromMemory(
148                         i, memoryPools[operand.location.poolIndex].get(), operand.location.offset,
149                         operand.location.length);
150                 break;
151             }
152             case OperandLifeTime::SUBGRAPH: {
153                 ErrorStatus otherErrorStatus = ErrorStatus::NONE;
154                 auto subgraph = convertSubgraphFromHAL(nnapi, memoryPools, model, allModels,
155                                                        operand.location.offset + 1,
156                                                        copiedOperandValues, &otherErrorStatus);
157                 if (subgraph) {
158                     resultModel.setOperandValueFromModel(i, subgraph);
159                 } else {
160                     LOG(ERROR) << "Failed to set subgraph operand value";
161                     *errorStatus = otherErrorStatus;
162                     return nullptr;
163                 }
164                 break;
165             }
166             case OperandLifeTime::NO_VALUE: {
167                 resultModel.setOperandValue(i, nullptr, 0);
168                 break;
169             }
170             case OperandLifeTime::TEMPORARY_VARIABLE:
171             case OperandLifeTime::SUBGRAPH_OUTPUT:
172             case OperandLifeTime::SUBGRAPH_INPUT: {
173                 break;
174             }
175             default:
176                 LOG(ERROR) << "Invalid operand type: " << static_cast<int>(operand.lifetime);
177                 *errorStatus = ErrorStatus::INVALID_ARGUMENT;
178                 return nullptr;
179         }
180 
181         if (!resultModel.isValid()) {
182             LOG(ERROR) << "Failed to add operand with index " << i;
183             *errorStatus = ErrorStatus::INVALID_ARGUMENT;
184             return nullptr;
185         }
186     }
187 
188     for (int i = 0; i < subgraph.operations.size(); ++i) {
189         const auto& operation = subgraph.operations[i];
190 
191         std::vector<uint32_t> inputs(operation.inputs.begin(), operation.inputs.end());
192         std::vector<uint32_t> outputs(operation.outputs.begin(), operation.outputs.end());
193 
194         int operationType = static_cast<int>(operation.type);
195         if (::android::nn::isExtension(static_cast<::android::nn::OperationType>(operationType))) {
196             uint16_t extensionPrefix =
197                     ::android::nn::getExtensionPrefix(static_cast<uint32_t>(operationType));
198             uint16_t typeWithinExtension =
199                     ::android::nn::getTypeWithinExtension(static_cast<uint32_t>(operationType));
200             auto* extensionName = getExtensionName(extensionPrefix);
201             if (extensionName == nullptr) {
202                 LOG(ERROR) << "Unknown extension prefix " << extensionPrefix;
203                 *errorStatus = ErrorStatus::INVALID_ARGUMENT;
204                 return nullptr;
205             }
206             resultModel.getExtensionOperationType(*extensionName, typeWithinExtension,
207                                                   &operationType);
208             if (!resultModel.isValid()) {
209                 LOG(ERROR) << "Failed to get extension operation with index " << i;
210                 *errorStatus = ErrorStatus::INVALID_ARGUMENT;
211                 return nullptr;
212             }
213         }
214 
215         resultModel.addOperation(operationType, inputs, outputs);
216 
217         if (!resultModel.isValid()) {
218             LOG(ERROR) << "Failed to add operation with index " << i;
219             *errorStatus = ErrorStatus::INVALID_ARGUMENT;
220             return nullptr;
221         }
222     }
223 
224     std::vector<uint32_t> inputIndexes(subgraph.inputIndexes.begin(), subgraph.inputIndexes.end());
225     std::vector<uint32_t> outputIndexes(subgraph.outputIndexes.begin(),
226                                         subgraph.outputIndexes.end());
227 
228     resultModel.identifyInputsAndOutputs(inputIndexes, outputIndexes);
229     if (!resultModel.isValid()) {
230         LOG(ERROR) << "Model identifyInputsAndOutputs failed";
231         *errorStatus = ErrorStatus::INVALID_ARGUMENT;
232         return nullptr;
233     }
234 
235     if (resultModel.finish() != Result::NO_ERROR) {
236         LOG(ERROR) << "Model finish failed";
237         *errorStatus = ErrorStatus::INVALID_ARGUMENT;
238         return nullptr;
239     }
240 
241     if (!resultModel.isValid()) {
242         LOG(ERROR) << "Invalid model";
243         *errorStatus = ErrorStatus::INVALID_ARGUMENT;
244         return nullptr;
245     }
246 
247     (*allModels)[subgraphIndex] = std::move(resultModel);
248     return (*allModels)[subgraphIndex]->getHandle();
249 }
250 
251 // This is needed for CONSTANT_COPY operands > 128 bytes, we have to
252 // store them in intenal buffer
needsCopiedOperandValues(const neuralnetworks::Model & model)253 bool needsCopiedOperandValues(const neuralnetworks::Model& model) {
254     for (int sindex = 0; sindex < model.referenced.size() + 1; ++sindex) {
255         const auto& subgraph = sindex == 0 ? model.main : model.referenced[sindex - 1];
256         for (int i = 0; i < subgraph.operands.size(); ++i) {
257             const auto& operand = subgraph.operands[i];
258 
259             if (operand.lifetime == OperandLifeTime::CONSTANT_COPY) {
260                 if (operand.location.length >
261                     ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES) {
262                     return true;
263                 }
264             }
265         }
266     }
267     return false;
268 }
269 
isValid(const Subgraph & subgraph)270 bool isValid(const Subgraph& subgraph) {
271     // Either the operand has a known value before model execution begins, or we've seen a writer
272     // for this operand while walking operands in execution order. Initialize to known operands.
273     std::vector<bool> operandValueKnown;
274     operandValueKnown.reserve(subgraph.operands.size());
275     std::transform(subgraph.operands.begin(), subgraph.operands.end(),
276                    std::back_inserter(operandValueKnown), [](const Operand& operand) {
277                        return operand.lifetime != OperandLifeTime::TEMPORARY_VARIABLE &&
278                               operand.lifetime != OperandLifeTime::SUBGRAPH_OUTPUT;
279                    });
280 
281     // Validate that operations are sorted into execution order.
282     //
283     // If there is a cycle in the graph, the operations will not
284     // appear to be sorted into execution order: Some operation will
285     // have an input for which operandValueKnown[] is false.
286     for (size_t i = 0; i < subgraph.operations.size(); ++i) {
287         const auto& operation = subgraph.operations[i];
288 
289         for (size_t j = 0; j < operation.inputs.size(); ++j) {
290             const uint32_t k = operation.inputs[j];
291             if (!operandValueKnown[k]) {
292                 LOG(ERROR) << "Operation " << i << " input " << j << " (operand " << k
293                            << ") is read before it is written";
294                 return false;
295             }
296         }
297 
298         for (size_t j = 0; j < operation.outputs.size(); ++j) {
299             const uint32_t k = operation.outputs[j];
300             // Assuming validateOperations() has not returned an error, we know that this output is
301             // TEMPORARY_VARIABLE or MODEL_OUTPUT, and so the only way operandValueKnown[k] can be
302             // true is if we've already seen a writer for this operand.
303             if (operandValueKnown[k]) {
304                 LOG(ERROR) << "Operation " << i << " output " << j << " (operand " << k
305                            << ") has already been written";
306                 return false;
307             }
308             operandValueKnown[k] = true;
309         }
310     }
311 
312     // Verify all operands are written.
313     for (size_t i = 0; i < subgraph.operands.size(); ++i) {
314         if (!operandValueKnown[i]) {
315             LOG(ERROR) << "Operand " << i << " is never written";
316             return false;
317         }
318         const auto& operand = subgraph.operands[i];
319 
320         if (operand.lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
321             if (std::find(subgraph.outputIndexes.begin(), subgraph.outputIndexes.end(), i) ==
322                 subgraph.outputIndexes.end()) {
323                 LOG(ERROR) << "Op with output liftime, but not on output list: " << i;
324                 return false;
325             }
326         }
327     }
328 
329     // Validate input and output lifetime
330     for (auto index : subgraph.inputIndexes) {
331         if (subgraph.operands[index].lifetime != OperandLifeTime::SUBGRAPH_INPUT) {
332             LOG(ERROR) << "Input with index" << index << " has invalid lifetime";
333             return false;
334         }
335     }
336     for (auto index : subgraph.outputIndexes) {
337         if (subgraph.operands[index].lifetime != OperandLifeTime::SUBGRAPH_OUTPUT) {
338             LOG(ERROR) << "Output with index" << index << " has invalid lifetime";
339             return false;
340         }
341     }
342 
343     // TODO(b/77871786): verify that every operation has at least one output operand that is read?
344     return true;
345 }
346 
347 }  // namespace
348 
isValid(const neuralnetworks::Model & model)349 bool isValid(const neuralnetworks::Model& model) {
350     return (isValid(model.main) &&
351             std::all_of(model.referenced.begin(), model.referenced.end(),
352                         [](const Subgraph& subgraph) { return isValid(subgraph); }));
353 }
354 
convertFromHAL(const NnApiSupportLibrary * nnapi,const neuralnetworks::Model & model,std::vector<uint8_t> * copiedOperandValues,ErrorStatus * errorStatus)355 std::optional<ShimConvertedModel> convertFromHAL(const NnApiSupportLibrary* nnapi,
356                                                  const neuralnetworks::Model& model,
357                                                  std::vector<uint8_t>* copiedOperandValues,
358                                                  ErrorStatus* errorStatus) {
359     CHECK(copiedOperandValues != nullptr);
360 
361     *errorStatus = ErrorStatus::NONE;
362 
363     // Using this pulls in OperationResolver and huge chunk of dependencies.
364     // TODO(172925288): Replace as followup work
365     //    if (!::aidl::android::hardware::neuralnetworks::utils::valid(model)) {
366     if (!isValid(model)) {
367         LOG(ERROR) << "Invalid HAL model, failed to convert into SL model";
368         *errorStatus = ErrorStatus::INVALID_ARGUMENT;
369         return std::nullopt;
370     }
371 
372     std::vector<std::unique_ptr<::android::nn::sl_wrapper::Memory>> memoryPools;
373     memoryPools.reserve(model.pools.size());
374     for (const auto& pool : model.pools) {
375         std::unique_ptr<::android::nn::sl_wrapper::Memory> memory = convertFromHAL(nnapi, pool);
376         if (!memory) {
377             LOG(ERROR) << "Failed to convert HAL memory into SL memory";
378             *errorStatus = ErrorStatus::INVALID_ARGUMENT;
379             return std::nullopt;
380         }
381         memoryPools.push_back(std::move(memory));
382     }
383 
384     std::vector<std::optional<::android::nn::sl_wrapper::Model>> allModels(model.referenced.size() +
385                                                                            1);
386 
387     if (needsCopiedOperandValues(model)) {
388         *copiedOperandValues = model.operandValues;
389     }
390 
391     for (size_t i = 0; i < allModels.size(); ++i) {
392         if (convertSubgraphFromHAL(nnapi, memoryPools, model, &allModels, i, *copiedOperandValues,
393                                    errorStatus) == nullptr) {
394             LOG(ERROR) << "Failed to convert HAL subgraphs into SL subgraphs, index: " << i;
395             // Error status already set by convertSubgraphFromHAL
396             return std::nullopt;
397         }
398     }
399 
400     std::vector<::android::nn::sl_wrapper::Model> result;
401     result.reserve(allModels.size());
402     for (size_t i = 0; i < allModels.size(); ++i) {
403         if (!allModels[i].has_value()) {
404             LOG(ERROR) << "Missing SL subgraph";
405             *errorStatus = ErrorStatus::INVALID_ARGUMENT;
406             return std::nullopt;
407         }
408         result.push_back(std::move(*allModels[i]));
409     }
410 
411     return ShimConvertedModel{.memory = std::move(memoryPools), .models = std::move(result)};
412 }
413 
convertFromHAL(const NnApiSupportLibrary * nnapi,const neuralnetworks::Memory & pool)414 std::unique_ptr<::android::nn::sl_wrapper::Memory> convertFromHAL(
415         const NnApiSupportLibrary* nnapi, const neuralnetworks::Memory& pool) {
416     using Tag = neuralnetworks::Memory::Tag;
417     switch (pool.getTag()) {
418         case Tag::ashmem: {
419             const auto& ashmem = pool.get<Tag::ashmem>();
420             size_t size = ashmem.size;
421             int fd = ashmem.fd.get();
422 
423             auto memory = std::make_unique<::android::nn::sl_wrapper::Memory>(
424                     nnapi, size, PROT_READ | PROT_WRITE, fd, 0, /*ownsFd=*/false);
425             if (!memory->isValid()) {
426                 return nullptr;
427             }
428             return memory;
429         }
430         case Tag::mappableFile: {
431             const auto& mappableFile = pool.get<Tag::mappableFile>();
432             size_t size = mappableFile.length;
433             int fd = mappableFile.fd.get();
434             int prot = mappableFile.prot & (PROT_READ | PROT_WRITE);
435             size_t offset = mappableFile.offset;
436 
437             auto memory = std::make_unique<::android::nn::sl_wrapper::Memory>(
438                     nnapi, size, prot, fd, offset, /*ownsFd=*/false);
439             if (!memory->isValid()) {
440                 return nullptr;
441             }
442             return memory;
443         }
444         case Tag::hardwareBuffer: {
445             const auto& hardwareBuffer = pool.get<Tag::hardwareBuffer>();
446 
447             native_handle_t* handle = ::android::dupFromAidl(hardwareBuffer.handle);
448             if (handle == nullptr) {
449                 LOG(ERROR) << "Dup of the hardware_buffer_blob memory pool failed";
450                 return nullptr;
451             }
452             const auto handleGuard = ::android::base::make_scope_guard([handle] {
453                 native_handle_close(handle);
454                 native_handle_delete(handle);
455             });
456             for (size_t i = 0; i < handle->numFds; ++i) {
457                 if (handle->data[i] == -1) {
458                     LOG(ERROR) << "Dup of the hardware_buffer_blob memory pool failed";
459                     return nullptr;
460                 }
461             }
462 
463             const AHardwareBuffer_Desc desc{
464                     .width = static_cast<uint32_t>(hardwareBuffer.description.width),
465                     .height = static_cast<uint32_t>(hardwareBuffer.description.height),
466                     .layers = static_cast<uint32_t>(hardwareBuffer.description.layers),
467                     .format = static_cast<uint32_t>(hardwareBuffer.description.format),
468                     .usage = static_cast<uint64_t>(hardwareBuffer.description.usage),
469                     .stride = static_cast<uint32_t>(hardwareBuffer.description.stride),
470             };
471             AHardwareBuffer* ahwb = nullptr;
472             const ::android::status_t status = AHardwareBuffer_createFromHandle(
473                     &desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &ahwb);
474             if (status != ::android::NO_ERROR) {
475                 LOG(ERROR) << "createFromHandle failed";
476                 return nullptr;
477             }
478 
479             const bool isBlob = desc.format == AHARDWAREBUFFER_FORMAT_BLOB;
480             const size_t size = isBlob ? desc.width : 0;
481 
482             // Takes ownership of hardwareBuffer, handle gets closed
483             auto memory =
484                     std::make_unique<::android::nn::sl_wrapper::Memory>(nnapi, ahwb,
485                                                                         /*ownAHB=*/true, size);
486             if (!memory->isValid()) {
487                 return nullptr;
488             }
489             return memory;
490         }
491     }
492     LOG(ERROR) << "Can't convert to SL Memory, unknown pool tag: " << pool.getTag();
493     return nullptr;
494 }
495 
496 }  // namespace aidl::android::hardware::neuralnetworks
497