• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ShimDevice"
18 
19 #include "ShimDevice.h"
20 
21 #include <NeuralNetworks.h>
22 #include <aidl/android/hardware/neuralnetworks/DataLocation.h>
23 #include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
24 #include <aidl/android/hardware/neuralnetworks/Extension.h>
25 #include <aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.h>
26 #include <aidl/android/hardware/neuralnetworks/Memory.h>
27 #include <aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.h>
28 #include <aidl/android/hardware/neuralnetworks/OperandLifeTime.h>
29 #include <aidl/android/hardware/neuralnetworks/OperandPerformance.h>
30 #include <android-base/logging.h>
31 #include <android-base/scopeguard.h>
32 #include <android/binder_auto_utils.h>
33 #include <android/binder_manager.h>
34 #include <android/binder_process.h>
35 #include <nnapi/TypeUtils.h>
36 #include <nnapi/hal/aidl/Conversions.h>
37 
38 #include <algorithm>
39 #include <limits>
40 #include <memory>
41 #include <optional>
42 #include <string>
43 #include <utility>
44 #include <vector>
45 
46 #include "ShimConverter.h"
47 #include "ShimPreparedModel.h"
48 #include "ShimUtils.h"
49 #include "SupportLibrary.h"
50 
51 using namespace ::android::nn::sl_wrapper;
52 
53 namespace aidl::android::hardware::neuralnetworks {
54 
55 namespace {
56 
convertToNDKPriority(Priority priority)57 constexpr std::optional<::android::nn::wrapper::ExecutePriority> convertToNDKPriority(
58         Priority priority) {
59     switch (priority) {
60         case Priority::LOW:
61             return ::android::nn::wrapper::ExecutePriority::LOW;
62         case Priority::MEDIUM:
63             return ::android::nn::wrapper::ExecutePriority::MEDIUM;
64         case Priority::HIGH:
65             return ::android::nn::wrapper::ExecutePriority::HIGH;
66     }
67     LOG(ERROR) << "unrecognized priority: " << static_cast<int32_t>(priority);
68     return std::nullopt;
69 }
70 
convertToNDKPreference(ExecutionPreference preference)71 constexpr std::optional<::android::nn::wrapper::ExecutePreference> convertToNDKPreference(
72         ExecutionPreference preference) {
73     switch (preference) {
74         case ExecutionPreference::LOW_POWER:
75             return ::android::nn::wrapper::ExecutePreference::PREFER_LOW_POWER;
76         case ExecutionPreference::FAST_SINGLE_ANSWER:
77             return ::android::nn::wrapper::ExecutePreference::PREFER_FAST_SINGLE_ANSWER;
78         case ExecutionPreference::SUSTAINED_SPEED:
79             return ::android::nn::wrapper::ExecutePreference::PREFER_SUSTAINED_SPEED;
80     }
81     LOG(ERROR) << "unrecognized preference: " << static_cast<int32_t>(preference);
82     return std::nullopt;
83 }
84 
85 // Safely downcast an IPreparedModel object to ShimPreparedModel.
86 // This function will return nullptr if the IPreparedModel object is not originated from the
87 // shim process.
castToShimPreparedModel(IPreparedModel * preparedModel)88 const ShimPreparedModel* castToShimPreparedModel(IPreparedModel* preparedModel) {
89     if (preparedModel->isRemote()) {
90         return nullptr;
91     }
92     // This static_cast is safe because ShimPreparedModel is the only class that implements
93     // the IPreparedModel interface in the sample driver process.
94     return static_cast<const ShimPreparedModel*>(preparedModel);
95 }
96 
convertPerformanceInfo(const SL_ANeuralNetworksPerformanceInfo & info)97 static PerformanceInfo convertPerformanceInfo(const SL_ANeuralNetworksPerformanceInfo& info) {
98     return {.execTime = info.execTime, .powerUsage = info.powerUsage};
99 }
100 
getCapabilities(const NnApiSupportLibrary * nnapi,ANeuralNetworksDevice * device)101 Capabilities getCapabilities(const NnApiSupportLibrary* nnapi, ANeuralNetworksDevice* device) {
102     Capabilities capabilities;
103     SL_ANeuralNetworksPerformanceInfo performanceInfo;
104 
105     nnapi->SL_ANeuralNetworksDevice_getPerformanceInfo(
106             device, SL_ANEURALNETWORKS_CAPABILITIES_PERFORMANCE_RELAXED_SCALAR, &performanceInfo);
107     capabilities.relaxedFloat32toFloat16PerformanceScalar = convertPerformanceInfo(performanceInfo);
108 
109     nnapi->SL_ANeuralNetworksDevice_getPerformanceInfo(
110             device, SL_ANEURALNETWORKS_CAPABILITIES_PERFORMANCE_RELAXED_TENSOR, &performanceInfo);
111     capabilities.relaxedFloat32toFloat16PerformanceTensor = convertPerformanceInfo(performanceInfo);
112 
113     nnapi->SL_ANeuralNetworksDevice_getPerformanceInfo(
114             device, SL_ANEURALNETWORKS_CAPABILITIES_PERFORMANCE_IF, &performanceInfo);
115     capabilities.ifPerformance = convertPerformanceInfo(performanceInfo);
116 
117     nnapi->SL_ANeuralNetworksDevice_getPerformanceInfo(
118             device, SL_ANEURALNETWORKS_CAPABILITIES_PERFORMANCE_WHILE, &performanceInfo);
119     capabilities.whilePerformance = convertPerformanceInfo(performanceInfo);
120 
121     constexpr auto fn = [](SL_ANeuralNetworksOperandPerformanceInfo info, void* context) {
122         auto* out = static_cast<std::vector<OperandPerformance>*>(context);
123         out->push_back(OperandPerformance{
124                 .type = static_cast<OperandType>(info.operandType),
125                 .info = convertPerformanceInfo(info.performanceInfo),
126         });
127     };
128 
129     nnapi->SL_ANeuralNetworksDevice_forEachOperandPerformanceInfo(
130             device, static_cast<void*>(&capabilities.operandPerformance), fn);
131 
132     return capabilities;
133 }
134 
getNumberOfCacheFilesNeeded(const NnApiSupportLibrary * nnapi,ANeuralNetworksDevice * device)135 NumberOfCacheFiles getNumberOfCacheFilesNeeded(const NnApiSupportLibrary* nnapi,
136                                                ANeuralNetworksDevice* device) {
137     uint32_t numModelCacheFiles;
138     uint32_t numDataCacheFiles;
139     nnapi->SL_ANeuralNetworksDevice_getNumberOfCacheFilesNeeded(device, &numModelCacheFiles,
140                                                                 &numDataCacheFiles);
141     return {
142             .numModelCache = static_cast<int32_t>(numModelCacheFiles),
143             .numDataCache = static_cast<int32_t>(numDataCacheFiles),
144     };
145 }
146 
getVendorExtensions(const NnApiSupportLibrary * nnapi,ANeuralNetworksDevice * device)147 std::vector<Extension> getVendorExtensions(const NnApiSupportLibrary* nnapi,
148                                            ANeuralNetworksDevice* device) {
149     uint32_t vendorExtensionCount;
150     nnapi->SL_ANeuralNetworksDevice_getVendorExtensionCount(device, &vendorExtensionCount);
151 
152     std::vector<Extension> extensions(vendorExtensionCount);
153 
154     for (uint32_t vendorExtensionIndex = 0; vendorExtensionIndex < vendorExtensionCount;
155          ++vendorExtensionIndex) {
156         auto& extension = extensions[vendorExtensionIndex];
157 
158         const char* extensionName;
159         nnapi->SL_ANeuralNetworksDevice_getVendorExtensionName(device, vendorExtensionIndex,
160                                                                &extensionName);
161         extension.name = extensionName;
162 
163         constexpr auto fn = [](SL_ANeuralNetworksExtensionOperandTypeInformation info,
164                                void* context) {
165             auto* out = static_cast<std::vector<ExtensionOperandTypeInformation>*>(context);
166             out->push_back(ExtensionOperandTypeInformation{
167                     .type = info.type,
168                     .isTensor = info.isTensor,
169                     .byteSize = static_cast<int32_t>(info.byteSize),
170             });
171         };
172         nnapi->SL_ANeuralNetworksDevice_forEachVendorExtensionOperandTypeInformation(
173                 device, vendorExtensionIndex, static_cast<void*>(&extension.operandTypes), fn);
174     }
175 
176     return extensions;
177 }
178 
179 }  // namespace
180 
ShimDevice(std::shared_ptr<const NnApiSupportLibrary> nnapi,ANeuralNetworksDevice * device,std::string serviceName)181 ShimDevice::ShimDevice(std::shared_ptr<const NnApiSupportLibrary> nnapi,
182                        ANeuralNetworksDevice* device, std::string serviceName)
183     : mNnapi(std::move(nnapi)),
184       mBufferTracker(ShimBufferTracker::create()),
185       mServiceName(std::move(serviceName)),
186       mDevice(device),
187       mCapabilities(neuralnetworks::getCapabilities(mNnapi.get(), mDevice)),
188       mNumberOfCacheFiles(neuralnetworks::getNumberOfCacheFilesNeeded(mNnapi.get(), mDevice)),
189       mExtensions(neuralnetworks::getVendorExtensions(mNnapi.get(), mDevice)) {}
190 
191 // Manages the data buffer for an operand.
192 class ShimBuffer : public BnBuffer {
193    public:
ShimBuffer(const NnApiSupportLibrary * nnApi,const::android::nn::Dimensions initialDimensions,const::android::nn::OperandType type,std::shared_ptr<::android::nn::sl_wrapper::Memory> memory,std::unique_ptr<ShimBufferTracker::Token> token)194     ShimBuffer(const NnApiSupportLibrary* nnApi, const ::android::nn::Dimensions initialDimensions,
195                const ::android::nn::OperandType type,
196                std::shared_ptr<::android::nn::sl_wrapper::Memory> memory,
197                std::unique_ptr<ShimBufferTracker::Token> token)
198         : kInitialDimensions(initialDimensions),
199           kType(type),
200           mNnApi(nnApi),
201           mMemory(std::move(memory)),
202           kToken(std::move(token)) {}
203 
tensorHasUnspecifiedDimensions(::android::nn::OperandType type,const::android::nn::Dimensions & dimensions)204     bool tensorHasUnspecifiedDimensions(::android::nn::OperandType type,
205                                         const ::android::nn::Dimensions& dimensions) {
206         if (!::android::nn::isExtension(type)) {
207             if (isNonExtensionScalar(type)) {
208                 return false;
209             }
210         }
211         return dimensions.size() == 0 || std::any_of(dimensions.begin(), dimensions.end(),
212                                                      [](int32_t dim) { return dim == 0; });
213     }
214 
validateDimensions(const::android::nn::Dimensions & dimensions)215     bool validateDimensions(const ::android::nn::Dimensions& dimensions) {
216         if (isNonExtensionScalar(kType)) {
217             if (!dimensions.empty()) {
218                 LOG(ERROR) << "ShimBuffer::validateDimensions -- invalid dimensions for scalar "
219                               "operand";
220                 return false;
221             }
222             return true;
223         }
224 
225         if (dimensions.empty()) {
226             if (tensorHasUnspecifiedDimensions(kType, kInitialDimensions)) {
227                 LOG(ERROR) << "ShimBuffer::validateDimensions -- the initial dimensions are not "
228                               "fully specified and no dimension update is provided: ";
229 
230                 return false;
231             }
232         } else {
233             if (tensorHasUnspecifiedDimensions(kType, dimensions)) {
234                 LOG(ERROR) << "ShimBuffer::validateDimensions -- the updated dimensions are not "
235                               "fully specified: ";
236 
237                 return false;
238             }
239         }
240 
241         const auto combined = ::android::nn::combineDimensions(kInitialDimensions, dimensions);
242         if (!combined.has_value()) {
243             LOG(ERROR) << "ShimBuffer::validateDimensions -- incompatible dimensions";
244             return false;
245         }
246         return true;
247     }
248 
copyFrom(const aidl::android::hardware::neuralnetworks::Memory & src,const std::vector<int32_t> & dimensions)249     ndk::ScopedAStatus copyFrom(const aidl::android::hardware::neuralnetworks::Memory& src,
250                                 const std::vector<int32_t>& dimensions) override {
251         auto memory = convertFromHAL(mNnApi, src);
252 
253         if (!memory) {
254             LOG(ERROR) << "Failed to convert HAL Memory to SL memory";
255             return toAStatus(ErrorStatus::INVALID_ARGUMENT);
256         }
257         const auto unsignedDimensions = ::android::nn::toUnsigned(dimensions);
258         if (!unsignedDimensions.has_value()) {
259             return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
260                              unsignedDimensions.error().message);
261         }
262 
263         if (!validateDimensions(unsignedDimensions.value())) {
264             LOG(ERROR) << "Invalid dimensions";
265             return toAStatus(ErrorStatus::INVALID_ARGUMENT);
266         }
267         Result result = memory->copyTo(*mMemory.get());
268 
269         // Special case expected error status for uninitialized source memory
270         if (result == Result::BAD_DATA) {
271             // NNAPI Runtime reports both uninitialized memory
272             // and incompatible dimensions as BAD_DATA, but
273             // VTS expects to see INVALID_ARGUMENT for bad dimensions,
274             // and GENERAL_FAILURE for uninitialized memory.
275             if (memory->getSize() != mMemory->getSize()) {
276                 return toAStatus(ErrorStatus::INVALID_ARGUMENT, "Incompatible sizes");
277             }
278 
279             return toAStatus(ErrorStatus::GENERAL_FAILURE);
280         }
281         SLW2SAS_RETURN_IF_ERROR(result);
282         return ndk::ScopedAStatus::ok();
283     }
284 
copyTo(const Memory & dst)285     ndk::ScopedAStatus copyTo(const Memory& dst) override {
286         auto memory = convertFromHAL(mNnApi, dst);
287 
288         if (!memory) {
289             LOG(ERROR) << "Failed to convert HAL Memory to SL memory";
290             return toAStatus(ErrorStatus::INVALID_ARGUMENT);
291         }
292 
293         Result result = mMemory->copyTo(*memory);
294         // Special case expected error status for uninitialized source memory
295         if (result == Result::BAD_DATA) {
296             // NNAPI Runtime reports both uninitialized memory
297             // and incompatible dimensions as BAD_DATA, but
298             // VTS expects to see INVALID_ARGUMENT for bad dimensions,
299             // and GENERAL_FAILURE for uninitialized memory.
300             if (memory->getSize() != mMemory->getSize()) {
301                 return toAStatus(ErrorStatus::INVALID_ARGUMENT, "Incompatible sizes");
302             }
303             return toAStatus(ErrorStatus::GENERAL_FAILURE);
304         }
305         SLW2SAS_RETURN_IF_ERROR(result);
306         return ndk::ScopedAStatus::ok();
307     }
308 
309    private:
310     const ::android::nn::Dimensions kInitialDimensions;
311     const ::android::nn::OperandType kType;
312 
313     const NnApiSupportLibrary* mNnApi;
314     std::shared_ptr<::android::nn::sl_wrapper::Memory> mMemory;
315     const std::unique_ptr<ShimBufferTracker::Token> kToken;
316 };
317 
allocate(const BufferDesc & desc,const std::vector<IPreparedModelParcel> & preparedModels,const std::vector<BufferRole> & inputRoles,const std::vector<BufferRole> & outputRoles,DeviceBuffer * buffer)318 ::ndk::ScopedAStatus ShimDevice::allocate(const BufferDesc& desc,
319                                           const std::vector<IPreparedModelParcel>& preparedModels,
320                                           const std::vector<BufferRole>& inputRoles,
321                                           const std::vector<BufferRole>& outputRoles,
322                                           DeviceBuffer* buffer) {
323     if (!isValidDimension(desc.dimensions)) {
324         LOG(ERROR) << "ShimDriver::allocate -- passed invalid dimension values.";
325         return toAStatus(ErrorStatus::INVALID_ARGUMENT,
326                          "ShimDriver::allocate -- passed invalid dimension values");
327     }
328     ANeuralNetworksMemoryDesc* slDesc = nullptr;
329     mNnapi->ANeuralNetworksMemoryDesc_create(&slDesc);
330     const auto slDescGuard = ::android::base::make_scope_guard(
331             [this, slDesc] { mNnapi->ANeuralNetworksMemoryDesc_free(slDesc); });
332 
333     auto unsignedDimensions = ::android::nn::toUnsigned(desc.dimensions).value();
334     if (mNnapi->ANeuralNetworksMemoryDesc_setDimensions(slDesc, desc.dimensions.size(),
335                                                         unsignedDimensions.data()) !=
336         ANEURALNETWORKS_NO_ERROR) {
337         LOG(ERROR) << "ShimDriver::allocate -- ANeuralNetworksMemoryDesc_setDimensions fail.";
338         return toAStatus(ErrorStatus::INVALID_ARGUMENT,
339                          "ShimDriver::allocate -- ANeuralNetworksMemoryDesc_setDimensions fail");
340     }
341 
342     constexpr auto getCompilation = [](IPreparedModel* preparedModel) -> const ShimPreparedModel* {
343         const auto* samplePreparedModel = castToShimPreparedModel(preparedModel);
344         if (samplePreparedModel == nullptr) {
345             LOG(ERROR) << "ShimDriver::allocate -- unknown remote IPreparedModel.";
346             return nullptr;
347         }
348         return samplePreparedModel;
349     };
350 
351     std::optional<::android::nn::OperandType> type;
352     std::vector<uint32_t> dimensions = ::android::nn::toUnsigned(desc.dimensions).value();
353 
354     for (const auto& role : inputRoles) {
355         if (role.modelIndex < 0 || role.modelIndex >= preparedModels.size()) {
356             LOG(ERROR) << "Invalid modelIndex value " << role.modelIndex;
357             return toAStatus(ErrorStatus::INVALID_ARGUMENT,
358                              "ShimDriver::allocate -- Input role modeIndex with invalid value");
359         }
360         auto preparedModel = preparedModels[role.modelIndex];
361         if (preparedModel.preparedModel == nullptr) {
362             return toAStatus(ErrorStatus::INVALID_ARGUMENT,
363                              "ShimDriver::allocate -- nullptr model");
364         }
365 
366         auto pmodel = getCompilation(preparedModel.preparedModel.get());
367         if (pmodel == nullptr) {
368             return toAStatus(ErrorStatus::INVALID_ARGUMENT,
369                              "ShimDriver::allocate -- nullptr model");
370         }
371 
372         auto result = mNnapi->ANeuralNetworksMemoryDesc_addInputRole(
373                 slDesc, pmodel->getCompilation().getHandle(), role.ioIndex, role.probability);
374 
375         if (result != ANEURALNETWORKS_NO_ERROR) {
376             LOG(ERROR) << "SampleDriver::allocate -- ANeuralNetworksMemoryDesc_addInputRole fail.";
377             return toAStatus(ErrorStatus::INVALID_ARGUMENT,
378                              "ShimDriver::allocate -- ANeuralNetworksMemoryDesc_addInputRole fail");
379         }
380 
381         const auto& model = pmodel->getMainModel();
382         const auto& op = model.getOperands()[model.getInputs()[role.ioIndex]];
383         auto operandType = static_cast<::android::nn::OperandType>(op.operandType.type);
384         if (!type) {
385             type = operandType;
386         }
387         if (dimensions.empty()) {
388             dimensions = op.dimensions;
389         }
390     }
391 
392     for (const auto& role : outputRoles) {
393         if (role.modelIndex < 0 || role.modelIndex >= preparedModels.size()) {
394             LOG(ERROR) << "Invalid modelIndex value " << role.modelIndex;
395             return toAStatus(ErrorStatus::INVALID_ARGUMENT,
396                              "ShimDriver::allocate -- Ou0tput role modeIndex with invalid value");
397         }
398         auto preparedModel = preparedModels[role.modelIndex];
399         if (preparedModel.preparedModel == nullptr) {
400             return toAStatus(ErrorStatus::INVALID_ARGUMENT,
401                              "ShimDriver::allocate -- nullptr model");
402         }
403 
404         auto pmodel = getCompilation(preparedModel.preparedModel.get());
405         if (pmodel == nullptr) {
406             return toAStatus(ErrorStatus::INVALID_ARGUMENT,
407                              "ShimDriver::allocate -- nullptr model");
408         }
409 
410         auto result = mNnapi->ANeuralNetworksMemoryDesc_addOutputRole(
411                 slDesc, pmodel->getCompilation().getHandle(), role.ioIndex, role.probability);
412 
413         if (result != ANEURALNETWORKS_NO_ERROR) {
414             LOG(ERROR) << "SampleDriver::allocate -- ANeuralNetworksMemoryDesc_addInputRole fail.";
415             return toAStatus(ErrorStatus::INVALID_ARGUMENT,
416                              "ShimDriver::allocate -- ANeuralNetworksMemoryDesc_addInputRole fail");
417         }
418         const auto& model = pmodel->getMainModel();
419         const auto& op = model.getOperands()[model.getInputs()[role.ioIndex]];
420         auto operandType = static_cast<::android::nn::OperandType>(op.operandType.type);
421         if (!type) {
422             type = operandType;
423         }
424         if (dimensions.empty()) {
425             dimensions = op.dimensions;
426         }
427     }
428 
429     auto typeSize = ::android::nn::getNonExtensionSize(*type, dimensions);
430     if (!typeSize.has_value()) {
431         return toAStatus(ErrorStatus::INVALID_ARGUMENT,
432                          "ShimDriver::allocate -- failed to get underlying type size, "
433                          "possibly an extension type");
434     }
435 
436     mNnapi->ANeuralNetworksMemoryDesc_finish(slDesc);
437     auto memory =
438             std::make_shared<::android::nn::sl_wrapper::Memory>(mNnapi.get(), slDesc, *typeSize);
439 
440     if (!memory->isValid()) {
441         LOG(ERROR) << "ShimDriver::allocate -- ANeuralNetworksMemory_createFromDesc failed.";
442         return toAStatus(ErrorStatus::GENERAL_FAILURE,
443                          "ShimDriver::allocate -- ANeuralNetworksMemory_createFromDesc failed");
444     }
445 
446     auto token = mBufferTracker->add(memory);
447     if (token == nullptr) {
448         LOG(ERROR) << "ShimDriver::allocate -- ShimBufferTracker returned invalid token.";
449         return toAStatus(ErrorStatus::GENERAL_FAILURE,
450                          "ShimDriver::allocate -- ShimBufferTracker returned invalid token.");
451     }
452     const uint32_t tokenValue = token->get();
453     auto shimbuffer = ndk::SharedRefBase::make<ShimBuffer>(mNnapi.get(), dimensions, *type,
454                                                            std::move(memory), std::move(token));
455     buffer->buffer = std::move(shimbuffer);
456     buffer->token = tokenValue;
457 
458     return ndk::ScopedAStatus::ok();
459 }
460 
getCapabilities(Capabilities * capabilities)461 ndk::ScopedAStatus ShimDevice::getCapabilities(Capabilities* capabilities) {
462     *capabilities = mCapabilities;
463     return ndk::ScopedAStatus::ok();
464 }
465 
getNumberOfCacheFilesNeeded(NumberOfCacheFiles * numberOfCacheFiles)466 ndk::ScopedAStatus ShimDevice::getNumberOfCacheFilesNeeded(NumberOfCacheFiles* numberOfCacheFiles) {
467     *numberOfCacheFiles = mNumberOfCacheFiles;
468     return ndk::ScopedAStatus::ok();
469 }
470 
getSupportedExtensions(std::vector<Extension> * extensions)471 ndk::ScopedAStatus ShimDevice::getSupportedExtensions(std::vector<Extension>* extensions) {
472     *extensions = mExtensions;
473     return ndk::ScopedAStatus::ok();
474 }
475 
getSupportedOperations(const Model & model,std::vector<bool> * supportedOperations)476 ndk::ScopedAStatus ShimDevice::getSupportedOperations(const Model& model,
477                                                       std::vector<bool>* supportedOperations) {
478     const auto numOperations = model.main.operations.size();
479     supportedOperations->resize(numOperations);
480 
481     ErrorStatus convertErrorStatus = ErrorStatus::NONE;
482     std::vector<uint8_t> copiedOperandValues;
483     auto modelAndMemory =
484             convertFromHAL(mNnapi.get(), model, &copiedOperandValues, &convertErrorStatus);
485     if (!modelAndMemory || modelAndMemory->models.empty()) {
486         LOG(ERROR) << "Failed to convert HAL model to SL model";
487         return toAStatus(convertErrorStatus);
488     }
489 
490     auto annModel = modelAndMemory->models[0].getHandle();
491     auto supportedOps = std::make_unique<bool[]>(numOperations);
492 
493     auto result = mNnapi->ANeuralNetworksModel_getSupportedOperationsForDevices(
494             annModel, &mDevice, /*numDevices=*/1, supportedOps.get());
495     SLW2SAS_RETURN_IF_ERROR(result);
496 
497     std::copy(supportedOps.get(), supportedOps.get() + numOperations, supportedOperations->begin());
498     return ndk::ScopedAStatus::ok();
499 }
500 
getType(DeviceType * type)501 ndk::ScopedAStatus ShimDevice::getType(DeviceType* type) {
502     int32_t deviceType;
503     auto result = mNnapi->ANeuralNetworksDevice_getType(mDevice, &deviceType);
504     SLW2SAS_RETURN_IF_ERROR(result);
505     *type = static_cast<DeviceType>(deviceType);
506     return ndk::ScopedAStatus::ok();
507 }
508 
getVersionString(std::string * versionString)509 ndk::ScopedAStatus ShimDevice::getVersionString(std::string* versionString) {
510     const char* buffer;
511     auto result = mNnapi->ANeuralNetworksDevice_getVersion(mDevice, &buffer);
512     SLW2SAS_RETURN_IF_ERROR(result);
513 
514     *versionString = std::string(buffer);
515     return ndk::ScopedAStatus::ok();
516 }
517 
getIntFds(const std::vector<::ndk::ScopedFileDescriptor> & scopedFds)518 static std::vector<int> getIntFds(const std::vector<::ndk::ScopedFileDescriptor>& scopedFds) {
519     std::vector<int> fds;
520     fds.reserve(scopedFds.size());
521     for (const auto& scopedFd : scopedFds) {
522         fds.push_back(scopedFd.get());
523     }
524     return fds;
525 }
526 
prepareModel(const Model & model,ExecutionPreference preference,Priority priority,int64_t deadlineNs,const std::vector<::ndk::ScopedFileDescriptor> & modelCache,const std::vector<::ndk::ScopedFileDescriptor> & dataCache,const std::vector<uint8_t> & token,const std::shared_ptr<IPreparedModelCallback> & callback)527 ndk::ScopedAStatus ShimDevice::prepareModel(
528         const Model& model, ExecutionPreference preference, Priority priority, int64_t deadlineNs,
529         const std::vector<::ndk::ScopedFileDescriptor>& modelCache,
530         const std::vector<::ndk::ScopedFileDescriptor>& dataCache,
531         const std::vector<uint8_t>& token,
532         const std::shared_ptr<IPreparedModelCallback>& callback) {
533     // TODO(183398748): Run model preparation in detached thread.
534     if (callback == nullptr) {
535         return toAStatus(ErrorStatus::INVALID_ARGUMENT);
536     }
537 
538     auto ndkPreference = convertToNDKPreference(preference);
539     if (!ndkPreference) {
540         callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
541         return toAStatus(ErrorStatus::INVALID_ARGUMENT);
542     }
543     auto ndkPriority = convertToNDKPriority(priority);
544     if (!ndkPriority) {
545         callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
546         return toAStatus(ErrorStatus::INVALID_ARGUMENT);
547     }
548 
549     ErrorStatus convertErrorStatus = ErrorStatus::NONE;
550     std::vector<uint8_t> copiedOperandValues;
551     auto modelAndMemory =
552             convertFromHAL(mNnapi.get(), model, &copiedOperandValues, &convertErrorStatus);
553 
554     if (!modelAndMemory || modelAndMemory->models.empty()) {
555         callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
556         return toAStatus(convertErrorStatus);
557     }
558 
559     // b/185976051, past this point we pretend that compilation is asynchronous, and in
560     /// case of error we return OK status, but communicate the error through the callback.
561     auto compilation = ::android::nn::sl_wrapper::Compilation::createForDevice(
562             mNnapi.get(), &modelAndMemory->models[0], mDevice);
563 
564     SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(compilation.first, callback);
565     SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(compilation.second.setPreference(*ndkPreference),
566                                                   callback);
567     SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(compilation.second.setPriority(*ndkPriority),
568                                                   callback);
569     if (deadlineNs > -1) {
570         std::chrono::time_point<::android::base::boot_clock> deadlinePoint(
571                 std::chrono::nanoseconds{deadlineNs});
572         const auto currentTime = ::android::base::boot_clock::now();
573         const auto timeoutDuration = std::chrono::nanoseconds(deadlinePoint - currentTime);
574         if (timeoutDuration <= std::chrono::nanoseconds::zero()) {
575             callback->notify(ErrorStatus::MISSED_DEADLINE_TRANSIENT, nullptr);
576             return ndk::ScopedAStatus::ok();
577         }
578         SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(
579                 compilation.second.setTimeout(std::max<uint64_t>(1, timeoutDuration.count())),
580                 callback);
581     }
582     if (!modelCache.empty() || !dataCache.empty()) {
583         SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(
584                 compilation.second.setCachingFromFds(getIntFds(modelCache), getIntFds(dataCache),
585                                                      token),
586                 callback);
587     }
588     SLW2SAS_OK_RETURN_AND_ERROR_CALLBACK_IF_ERROR(compilation.second.finish(), callback);
589 
590     const std::shared_ptr<ShimPreparedModel> preparedModel =
591             ndk::SharedRefBase::make<ShimPreparedModel>(
592                     mNnapi, mBufferTracker, std::move(compilation.second),
593                     std::move(modelAndMemory->models), std::move(modelAndMemory->memory),
594                     std::move(copiedOperandValues));
595 
596     callback->notify(ErrorStatus::NONE, preparedModel);
597     return ndk::ScopedAStatus::ok();
598 }
599 
prepareModelFromCache(int64_t,const std::vector<::ndk::ScopedFileDescriptor> &,const std::vector<::ndk::ScopedFileDescriptor> &,const std::vector<uint8_t> &,const std::shared_ptr<IPreparedModelCallback> & callback)600 ndk::ScopedAStatus ShimDevice::prepareModelFromCache(
601         int64_t /*deadlineNs*/, const std::vector<::ndk::ScopedFileDescriptor>& /*modelCache*/,
602         const std::vector<::ndk::ScopedFileDescriptor>& /*dataCache*/,
603         const std::vector<uint8_t>& /*token*/,
604         const std::shared_ptr<IPreparedModelCallback>& callback) {
605     // The NNAPI runtime will attempt to call this before falling back to
606     // ShimDevice::prepareModel(). This is not a LOG(ERROR) to avoid producing
607     // misleading logcat messages on every compilation request because there is
608     // technically nothing wrong.
609     LOG(DEBUG) << "ShimDevice::prepareModelFromCache() is not supported. Use "
610                   "ShimDevice::prepareModel() instead.";
611     const auto ret = callback->notify(ErrorStatus::GENERAL_FAILURE, nullptr);
612     return toAStatus(ErrorStatus::GENERAL_FAILURE);
613 }
614 
615 }  // namespace aidl::android::hardware::neuralnetworks
616