1 /*
2  * Copyright (C) 2017 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 "SampleDriverAidl"
18 
19 #include "SampleDriverAidl.h"
20 
21 #include <android-base/logging.h>
22 #include <android-base/properties.h>
23 #include <android-base/scopeguard.h>
24 #include <android/binder_auto_utils.h>
25 #include <android/binder_interface_utils.h>
26 #include <nnapi/Result.h>
27 #include <nnapi/Types.h>
28 #include <nnapi/Validation.h>
29 #include <nnapi/hal/aidl/BufferTracker.h>
30 #include <nnapi/hal/aidl/Conversions.h>
31 #include <nnapi/hal/aidl/HalUtils.h>
32 #include <nnapi/hal/aidl/Utils.h>
33 
34 #include <algorithm>
35 #include <chrono>
36 #include <map>
37 #include <memory>
38 #include <optional>
39 #include <set>
40 #include <string>
41 #include <thread>
42 #include <tuple>
43 #include <utility>
44 #include <variant>
45 #include <vector>
46 
47 #include "CpuExecutor.h"
48 #include "SampleDriverAidlUtils.h"
49 #include "Tracing.h"
50 #include "Utils.h"
51 
52 namespace android {
53 namespace nn {
54 namespace sample_driver_aidl {
55 
56 namespace {
57 
nanosecondsDuration(TimePoint end,TimePoint start)58 int64_t nanosecondsDuration(TimePoint end, TimePoint start) {
59     return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
60 };
61 
62 constexpr aidl_hal::Timing kNoTiming = {.timeOnDeviceNs = -1, .timeInDriverNs = -1};
63 
convertResultCodeToAidlErrorStatus(int resultCode)64 aidl_hal::ErrorStatus convertResultCodeToAidlErrorStatus(int resultCode) {
65     const auto errorStatus = aidl_hal::utils::convert(convertResultCodeToErrorStatus(resultCode));
66     CHECK(errorStatus.has_value()) << "Unhandled error (" << errorStatus.error().code
67                                    << "): " << errorStatus.error().message;
68     return errorStatus.value();
69 }
70 
71 }  // namespace
72 
getVersionString(std::string * versionString)73 ndk::ScopedAStatus SampleDriver::getVersionString(std::string* versionString) {
74     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
75                  "SampleDriver::getVersionString");
76     *versionString = "JUST_AN_EXAMPLE";
77     return ndk::ScopedAStatus::ok();
78 }
79 
getType(aidl_hal::DeviceType * deviceType)80 ndk::ScopedAStatus SampleDriver::getType(aidl_hal::DeviceType* deviceType) {
81     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION, "SampleDriver::getType");
82     *deviceType = aidl_hal::DeviceType::CPU;
83     return ndk::ScopedAStatus::ok();
84 }
85 
getSupportedExtensions(std::vector<aidl_hal::Extension> * supportedExtensions)86 ndk::ScopedAStatus SampleDriver::getSupportedExtensions(
87         std::vector<aidl_hal::Extension>* supportedExtensions) {
88     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
89                  "SampleDriver::getSupportedExtensions");
90     *supportedExtensions = {/* No extensions. */};
91     return ndk::ScopedAStatus::ok();
92 }
93 
getNumberOfCacheFilesNeeded(aidl_hal::NumberOfCacheFiles * numberOfCacheFiles)94 ndk::ScopedAStatus SampleDriver::getNumberOfCacheFilesNeeded(
95         aidl_hal::NumberOfCacheFiles* numberOfCacheFiles) {
96     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
97                  "SampleDriver::getNumberOfCacheFilesNeeded");
98     // Set both numbers to be 0 for cache not supported.
99     numberOfCacheFiles->numDataCache = 0;
100     numberOfCacheFiles->numModelCache = 0;
101     return ndk::ScopedAStatus::ok();
102 }
103 
prepareModel(const aidl_hal::Model & model,aidl_hal::ExecutionPreference preference,aidl_hal::Priority priority,int64_t deadlineNs,const std::vector<ndk::ScopedFileDescriptor> &,const std::vector<ndk::ScopedFileDescriptor> &,const std::vector<uint8_t> &,const std::shared_ptr<aidl_hal::IPreparedModelCallback> & callback)104 ndk::ScopedAStatus SampleDriver::prepareModel(
105         const aidl_hal::Model& model, aidl_hal::ExecutionPreference preference,
106         aidl_hal::Priority priority, int64_t deadlineNs,
107         const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
108         const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
109         const std::vector<uint8_t>& /*token*/,
110         const std::shared_ptr<aidl_hal::IPreparedModelCallback>& callback) {
111     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION, "SampleDriver::prepareModel");
112     auto copiedModel = aidl_hal::utils::clone(model);
113     if (!copiedModel.has_value()) {
114         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, copiedModel.error().message);
115     }
116     return prepareModelBase(std::move(copiedModel).value(), this, preference, priority, deadlineNs,
117                             callback);
118 }
prepareModelWithConfig(const aidl_hal::Model & model,const aidl_hal::PrepareModelConfig & config,const std::shared_ptr<aidl_hal::IPreparedModelCallback> & callback)119 ndk::ScopedAStatus SampleDriver::prepareModelWithConfig(
120         const aidl_hal::Model& model, const aidl_hal::PrepareModelConfig& config,
121         const std::shared_ptr<aidl_hal::IPreparedModelCallback>& callback) {
122     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION,
123                  "SampleDriver::prepareModelWithConfig");
124     auto copiedModel = aidl_hal::utils::clone(model);
125     if (!copiedModel.has_value()) {
126         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, copiedModel.error().message);
127     }
128     return prepareModelBase(std::move(copiedModel).value(), this, config.preference,
129                             config.priority, config.deadlineNs, callback);
130 }
131 
prepareModelFromCache(int64_t,const std::vector<ndk::ScopedFileDescriptor> &,const std::vector<ndk::ScopedFileDescriptor> &,const std::vector<uint8_t> &,const std::shared_ptr<aidl_hal::IPreparedModelCallback> & callback)132 ndk::ScopedAStatus SampleDriver::prepareModelFromCache(
133         int64_t /*deadlineNs*/, const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
134         const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
135         const std::vector<uint8_t>& /*token*/,
136         const std::shared_ptr<aidl_hal::IPreparedModelCallback>& callback) {
137     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION,
138                  "SampleDriver::prepareModelFromCache");
139     notify(callback, aidl_hal::ErrorStatus::GENERAL_FAILURE, nullptr);
140     return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
141                      "Caching is not supported in the sample driver.");
142 }
143 
144 // Safely downcast an IPreparedModel object to SamplePreparedModel.
145 // This function will return nullptr if the IPreparedModel object is not originated from the sample
146 // driver process.
castToSamplePreparedModel(const std::shared_ptr<aidl_hal::IPreparedModel> & preparedModel)147 static const SamplePreparedModel* castToSamplePreparedModel(
148         const std::shared_ptr<aidl_hal::IPreparedModel>& preparedModel) {
149     if (preparedModel->isRemote()) {
150         return nullptr;
151     } else {
152         // This static_cast is safe because SamplePreparedModel is the only class that implements
153         // the IPreparedModel interface in the sample driver process.
154         return static_cast<const SamplePreparedModel*>(preparedModel.get());
155     }
156 }
157 
allocate(const aidl_hal::BufferDesc & desc,const std::vector<aidl_hal::IPreparedModelParcel> & halPreparedModels,const std::vector<aidl_hal::BufferRole> & inputRoles,const std::vector<aidl_hal::BufferRole> & outputRoles,aidl_hal::DeviceBuffer * buffer)158 ndk::ScopedAStatus SampleDriver::allocate(
159         const aidl_hal::BufferDesc& desc,
160         const std::vector<aidl_hal::IPreparedModelParcel>& halPreparedModels,
161         const std::vector<aidl_hal::BufferRole>& inputRoles,
162         const std::vector<aidl_hal::BufferRole>& outputRoles, aidl_hal::DeviceBuffer* buffer) {
163     VLOG(DRIVER) << "SampleDriver::allocate";
164     constexpr auto getModel = [](const std::shared_ptr<aidl_hal::IPreparedModel>& preparedModel)
165             -> const aidl_hal::Model* {
166         const auto* samplePreparedModel = castToSamplePreparedModel(preparedModel);
167         if (samplePreparedModel == nullptr) {
168             LOG(ERROR) << "SampleDriver::allocate -- unknown remote IPreparedModel.";
169             return nullptr;
170         }
171         return samplePreparedModel->getModel();
172     };
173 
174     std::vector<std::shared_ptr<aidl_hal::IPreparedModel>> preparedModels;
175     preparedModels.reserve(halPreparedModels.size());
176     for (const auto& halPreparedModelParcel : halPreparedModels) {
177         preparedModels.push_back(halPreparedModelParcel.preparedModel);
178     }
179     std::set<AidlHalPreparedModelRole> roles;
180     aidl_hal::Operand operand;
181     if (!validateMemoryDesc(desc, preparedModels, inputRoles, outputRoles, getModel, &roles,
182                             &operand)) {
183         LOG(ERROR) << "SampleDriver::allocate -- validation failed.";
184         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
185                          "SampleDriver::allocate -- validation failed.");
186     }
187 
188     if (isExtensionOperandType(operand.type)) {
189         LOG(ERROR) << "SampleDriver::allocate -- does not support extension type.";
190         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
191                          "SampleDriver::allocate -- does not support extension type.");
192     }
193 
194     // TODO(xusongw): Support allocating buffers with unknown dimensions or rank.
195 
196     // An operand obtained from validateMemoryDesc is guaranteed to be representable in canonical
197     // types.
198     uint32_t size = nonExtensionOperandSizeOfData(convert(operand.type).value(),
199                                                   toUnsigned(operand.dimensions).value());
200     VLOG(DRIVER) << "SampleDriver::allocate -- type = " << toString(operand.type)
201                  << ", dimensions = " << toString(operand.dimensions) << ", size = " << size;
202     if (size == 0) {
203         LOG(ERROR) << "SampleDriver::allocate -- does not support dynamic output shape.";
204         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
205                          "SampleDriver::allocate -- does not support dynamic output shape.");
206     }
207 
208     // An operand obtained from validateMemoryDesc is guaranteed to be representable in canonical
209     // types, so it safe to do an unvalidated conversion here.
210     auto bufferWrapper =
211             AidlManagedBuffer::create(size, std::move(roles), unvalidatedConvert(operand).value());
212     if (bufferWrapper == nullptr) {
213         LOG(ERROR) << "SampleDriver::allocate -- not enough memory.";
214         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
215                          "SampleDriver::allocate -- not enough memory.");
216     }
217 
218     auto token = mBufferTracker->add(bufferWrapper);
219     if (token == nullptr) {
220         LOG(ERROR) << "SampleDriver::allocate -- AidlBufferTracker returned invalid token.";
221         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
222                          "SampleDriver::allocate -- AidlBufferTracker returned invalid token.");
223     }
224 
225     const uint32_t tokenValue = token->get();
226     std::shared_ptr<SampleBuffer> sampleBuffer =
227             ndk::SharedRefBase::make<SampleBuffer>(std::move(bufferWrapper), std::move(token));
228     VLOG(DRIVER) << "SampleDriver::allocate -- successfully allocates the requested memory";
229     buffer->buffer = std::move(sampleBuffer);
230     buffer->token = tokenValue;
231     return ndk::ScopedAStatus::ok();
232 }
233 
copyRunTimePoolInfos(const RunTimePoolInfo & srcPool,const RunTimePoolInfo & dstPool)234 static void copyRunTimePoolInfos(const RunTimePoolInfo& srcPool, const RunTimePoolInfo& dstPool) {
235     CHECK(srcPool.getBuffer() != nullptr);
236     CHECK(dstPool.getBuffer() != nullptr);
237     CHECK(srcPool.getSize() == dstPool.getSize());
238     std::copy(srcPool.getBuffer(), srcPool.getBuffer() + srcPool.getSize(), dstPool.getBuffer());
239     dstPool.flush();
240 }
241 
copyTo(const aidl_hal::Memory & dst)242 ndk::ScopedAStatus SampleBuffer::copyTo(const aidl_hal::Memory& dst) {
243     const auto canonicalMemory = convert(dst);
244     if (!canonicalMemory.has_value()) {
245         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, canonicalMemory.error().message);
246     }
247     const auto dstPool = RunTimePoolInfo::createFromMemory(canonicalMemory.value());
248     if (!dstPool.has_value()) {
249         LOG(ERROR) << "SampleBuffer::copyTo -- unable to map dst memory.";
250         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
251                          "SampleBuffer::copyTo -- unable to map dst memory.");
252     }
253     const auto validationStatus =
254             aidl_hal::utils::convert(kBuffer->validateCopyTo(dstPool->getSize())).value();
255     if (validationStatus != aidl_hal::ErrorStatus::NONE) {
256         return toAStatus(validationStatus);
257     }
258     const auto srcPool =
259             RunTimePoolInfo::createFromExistingBuffer(kBuffer->getPointer(), kBuffer->getSize());
260     copyRunTimePoolInfos(srcPool, dstPool.value());
261     return ndk::ScopedAStatus::ok();
262 }
263 
copyFromInternal(const aidl_hal::Memory & src,const std::vector<uint32_t> & dimensions,const std::shared_ptr<AidlManagedBuffer> & bufferWrapper)264 static aidl_hal::ErrorStatus copyFromInternal(
265         const aidl_hal::Memory& src, const std::vector<uint32_t>& dimensions,
266         const std::shared_ptr<AidlManagedBuffer>& bufferWrapper) {
267     CHECK(bufferWrapper != nullptr);
268     const auto canonicalMemory = convert(src);
269     if (!canonicalMemory.has_value()) {
270         return aidl_hal::ErrorStatus::INVALID_ARGUMENT;
271     }
272     const auto srcPool = RunTimePoolInfo::createFromMemory(canonicalMemory.value());
273     if (!srcPool.has_value()) {
274         LOG(ERROR) << "SampleBuffer::copyFrom -- unable to map src memory.";
275         return aidl_hal::ErrorStatus::GENERAL_FAILURE;
276     }
277     const auto validationStatus = aidl_hal::utils::convert(bufferWrapper->validateCopyFrom(
278                                                                    dimensions, srcPool->getSize()))
279                                           .value();
280     if (validationStatus != aidl_hal::ErrorStatus::NONE) {
281         return validationStatus;
282     }
283     const auto dstPool = RunTimePoolInfo::createFromExistingBuffer(bufferWrapper->getPointer(),
284                                                                    bufferWrapper->getSize());
285     copyRunTimePoolInfos(srcPool.value(), dstPool);
286     return aidl_hal::ErrorStatus::NONE;
287 }
288 
copyFrom(const aidl_hal::Memory & src,const std::vector<int32_t> & dimensions)289 ndk::ScopedAStatus SampleBuffer::copyFrom(const aidl_hal::Memory& src,
290                                           const std::vector<int32_t>& dimensions) {
291     const auto unsignedDimensions = toUnsigned(dimensions);
292     if (!unsignedDimensions.has_value()) {
293         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
294                          unsignedDimensions.error().message);
295     }
296     const auto status = copyFromInternal(src, unsignedDimensions.value(), kBuffer);
297     if (status != aidl_hal::ErrorStatus::NONE) {
298         kBuffer->setInitialized(false);
299         return toAStatus(status);
300     }
301     kBuffer->updateDimensions(unsignedDimensions.value());
302     kBuffer->setInitialized(true);
303     return ndk::ScopedAStatus::ok();
304 }
305 
initialize()306 bool SamplePreparedModel::initialize() {
307     const auto canonicalPools = convert(mModel.pools);
308     if (!canonicalPools.has_value()) {
309         return false;
310     }
311     return setRunTimePoolInfosFromCanonicalMemories(&mPoolInfos, canonicalPools.value());
312 }
313 
314 static std::tuple<aidl_hal::ErrorStatus, std::vector<RunTimePoolInfo>,
315                   std::vector<std::shared_ptr<AidlManagedBuffer>>>
createRunTimePoolInfos(const Request & request,const SampleDriver & driver,const SamplePreparedModel * preparedModel)316 createRunTimePoolInfos(const Request& request, const SampleDriver& driver,
317                        const SamplePreparedModel* preparedModel) {
318     std::vector<RunTimePoolInfo> requestPoolInfos;
319     std::vector<std::shared_ptr<AidlManagedBuffer>> bufferWrappers;
320     requestPoolInfos.reserve(request.pools.size());
321     bufferWrappers.reserve(request.pools.size());
322     for (uint32_t i = 0; i < request.pools.size(); i++) {
323         const auto& pool = request.pools[i];
324         if (const auto* memory = std::get_if<SharedMemory>(&pool)) {
325             auto buffer = RunTimePoolInfo::createFromMemory(*memory);
326             if (!buffer.has_value()) {
327                 LOG(ERROR) << "createRuntimeMemoriesFromMemoryPools -- could not map pools";
328                 return {aidl_hal::ErrorStatus::GENERAL_FAILURE, {}, {}};
329             }
330             requestPoolInfos.push_back(std::move(*buffer));
331             bufferWrappers.push_back(nullptr);
332         } else if (const auto* token = std::get_if<Request::MemoryDomainToken>(&pool)) {
333             auto bufferWrapper = driver.getBufferTracker()->get(static_cast<uint32_t>(*token));
334             if (bufferWrapper == nullptr) {
335                 return {aidl_hal::ErrorStatus::INVALID_ARGUMENT, {}, {}};
336             }
337             const auto validationStatus =
338                     aidl_hal::utils::convert(
339                             bufferWrapper->validateRequest(i, request, preparedModel))
340                             .value();
341             if (validationStatus != aidl_hal::ErrorStatus::NONE) {
342                 return {validationStatus, {}, {}};
343             }
344             requestPoolInfos.push_back(RunTimePoolInfo::createFromExistingBuffer(
345                     bufferWrapper->getPointer(), bufferWrapper->getSize()));
346             bufferWrappers.push_back(std::move(bufferWrapper));
347         } else {
348             // If the pool is not a Memory or a token, the input is invalid.
349             return {aidl_hal::ErrorStatus::INVALID_ARGUMENT, {}, {}};
350         }
351     }
352     return {aidl_hal::ErrorStatus::NONE, std::move(requestPoolInfos), std::move(bufferWrappers)};
353 }
354 
updateDeviceMemories(aidl_hal::ErrorStatus status,const Request & request,const std::vector<std::shared_ptr<AidlManagedBuffer>> & bufferWrappers,const std::vector<aidl_hal::OutputShape> & outputShapes)355 static aidl_hal::ErrorStatus updateDeviceMemories(
356         aidl_hal::ErrorStatus status, const Request& request,
357         const std::vector<std::shared_ptr<AidlManagedBuffer>>& bufferWrappers,
358         const std::vector<aidl_hal::OutputShape>& outputShapes) {
359     if (status == aidl_hal::ErrorStatus::NONE) {
360         for (uint32_t i = 0; i < request.outputs.size(); i++) {
361             const uint32_t poolIndex = request.outputs[i].location.poolIndex;
362             const auto& pool = request.pools[poolIndex];
363             if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
364                 const auto unsignedDimensions = toUnsigned(outputShapes[i].dimensions).value();
365                 if (!bufferWrappers[poolIndex]->updateDimensions(unsignedDimensions)) {
366                     return aidl_hal::ErrorStatus::GENERAL_FAILURE;
367                 }
368             }
369         }
370         for (uint32_t i = 0; i < request.outputs.size(); i++) {
371             const uint32_t poolIndex = request.outputs[i].location.poolIndex;
372             const auto& pool = request.pools[poolIndex];
373             if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
374                 bufferWrappers[poolIndex]->setInitialized(true);
375             }
376         }
377     } else if (status == aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
378         // If CpuExecutor reports OUTPUT_INSUFFCIENT_SIZE on a device memory, this is because the
379         // dimensions of the device memory are incorrectly specified. The driver should return
380         // GENERAL_FAILURE instead in this case.
381         for (uint32_t i = 0; i < request.outputs.size(); i++) {
382             const uint32_t poolIndex = request.outputs[i].location.poolIndex;
383             const auto& pool = request.pools[poolIndex];
384             if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
385                 if (!outputShapes[i].isSufficient) {
386                     LOG(ERROR) << "Invalid dimensions for output " << i
387                                << ": actual shape = " << toString(outputShapes[i].dimensions);
388                     return aidl_hal::ErrorStatus::GENERAL_FAILURE;
389                 }
390             }
391         }
392     }
393     return aidl_hal::ErrorStatus::NONE;
394 }
395 
executeSynchronously(const aidl_hal::Request & halRequest,bool measureTiming,int64_t halDeadlineNs,int64_t loopTimeoutDurationNs,aidl_hal::ExecutionResult * executionResult)396 ndk::ScopedAStatus SamplePreparedModel::executeSynchronously(
397         const aidl_hal::Request& halRequest, bool measureTiming, int64_t halDeadlineNs,
398         int64_t loopTimeoutDurationNs, aidl_hal::ExecutionResult* executionResult) {
399     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
400                  "SampleDriver::executeSynchronously");
401     VLOG(DRIVER) << "executeSynchronously(" << SHOW_IF_DEBUG(halRequest.toString()) << ")";
402 
403     TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
404     if (measureTiming) driverStart = Clock::now();
405 
406     const auto model = convert(mModel).value();
407 
408     auto maybeRequest = convert(halRequest);
409     if (!maybeRequest.has_value()) {
410         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, maybeRequest.error().message);
411     }
412     const auto request = std::move(maybeRequest).value();
413 
414     const auto validationResult = validateRequestForModel(request, model);
415     if (!validationResult.ok()) {
416         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, validationResult.error());
417     }
418 
419     if (halDeadlineNs < -1) {
420         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
421                          "Invalid deadline: " + toString(halDeadlineNs));
422     }
423     if (loopTimeoutDurationNs < -1) {
424         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
425                          "Invalid loop timeout duration: " + toString(loopTimeoutDurationNs));
426     }
427 
428     const auto deadline = makeDeadline(halDeadlineNs);
429     if (hasDeadlinePassed(deadline)) {
430         return toAStatus(aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT);
431     }
432 
433     NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INPUTS_AND_OUTPUTS,
434                         "SampleDriver::executeSynchronouslyBase");
435     const auto [poolStatus, requestPoolInfos, bufferWrappers] =
436             createRunTimePoolInfos(request, *mDriver, this);
437     if (poolStatus != aidl_hal::ErrorStatus::NONE) {
438         return toAStatus(poolStatus);
439     }
440 
441     NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
442                         "SampleDriver::executeSynchronouslyBase");
443     CpuExecutor executor = mDriver->getExecutor();
444     if (loopTimeoutDurationNs >= 0) {
445         executor.setLoopTimeout(loopTimeoutDurationNs);
446     }
447     if (deadline.has_value()) {
448         executor.setDeadline(*deadline);
449     }
450     if (measureTiming) deviceStart = Clock::now();
451     int n = executor.run(model, request, mPoolInfos, requestPoolInfos);
452     if (measureTiming) deviceEnd = Clock::now();
453     VLOG(DRIVER) << "executor.run returned " << n;
454     aidl_hal::ErrorStatus executionStatus = convertResultCodeToAidlErrorStatus(n);
455     if (executionStatus != aidl_hal::ErrorStatus::NONE &&
456         executionStatus != aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
457         return toAStatus(executionStatus);
458     }
459     auto outputShapes = aidl_hal::utils::convert(executor.getOutputShapes()).value();
460 
461     // Update device memory metadata.
462     const aidl_hal::ErrorStatus updateStatus =
463             updateDeviceMemories(executionStatus, request, bufferWrappers, outputShapes);
464     if (updateStatus != aidl_hal::ErrorStatus::NONE) {
465         return toAStatus(updateStatus);
466     }
467 
468     executionResult->outputSufficientSize =
469             executionStatus != aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
470     executionResult->outputShapes = std::move(outputShapes);
471     executionResult->timing = kNoTiming;
472     if (measureTiming && executionStatus == aidl_hal::ErrorStatus::NONE) {
473         driverEnd = Clock::now();
474         aidl_hal::Timing timing = {.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
475                                    .timeInDriverNs = nanosecondsDuration(driverEnd, driverStart)};
476         VLOG(DRIVER) << "executeSynchronously timing = " << timing.toString();
477 
478         executionResult->timing = timing;
479     }
480     return ndk::ScopedAStatus::ok();
481 }
482 
483 // The sample driver will finish the execution and then return.
executeFenced(const aidl_hal::Request & halRequest,const std::vector<ndk::ScopedFileDescriptor> & waitFor,bool measureTiming,int64_t halDeadlineNs,int64_t loopTimeoutDurationNs,int64_t durationNs,aidl_hal::FencedExecutionResult * executionResult)484 ndk::ScopedAStatus SamplePreparedModel::executeFenced(
485         const aidl_hal::Request& halRequest, const std::vector<ndk::ScopedFileDescriptor>& waitFor,
486         bool measureTiming, int64_t halDeadlineNs, int64_t loopTimeoutDurationNs,
487         int64_t durationNs, aidl_hal::FencedExecutionResult* executionResult) {
488     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
489                  "SamplePreparedModel::executeFenced");
490     VLOG(DRIVER) << "executeFenced(" << SHOW_IF_DEBUG(halRequest.toString()) << ")";
491 
492     TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
493     if (measureTiming) driverStart = Clock::now();
494 
495     const auto model = convert(mModel).value();
496 
497     auto maybeRequest = convert(halRequest);
498     if (!maybeRequest.has_value()) {
499         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, maybeRequest.error().message);
500     }
501     const auto request = std::move(maybeRequest).value();
502 
503     const auto validationResult =
504             validateRequestForModel(request, model, /*allowUnspecifiedOutput=*/false);
505     if (!validationResult.ok()) {
506         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, validationResult.error());
507     }
508 
509     if (halDeadlineNs < -1) {
510         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
511                          "Invalid deadline: " + toString(halDeadlineNs));
512     }
513     if (loopTimeoutDurationNs < -1) {
514         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
515                          "Invalid loop timeout duration: " + toString(loopTimeoutDurationNs));
516     }
517     if (durationNs < -1) {
518         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
519                          "Invalid fenced execution duration: " + toString(durationNs));
520     }
521 
522     const auto deadline = makeDeadline(halDeadlineNs);
523     if (hasDeadlinePassed(deadline)) {
524         return toAStatus(aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT);
525     }
526 
527     // Wait for the dependent events to signal
528     for (const auto& fenceHandle : waitFor) {
529         int syncFenceFd = fenceHandle.get();
530         if (syncWait(syncFenceFd, -1) != FenceState::SIGNALED) {
531             LOG(ERROR) << "syncWait failed";
532             return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "syncWait failed");
533         }
534     }
535 
536     // Update deadline if the timeout duration is closer than the deadline.
537     auto closestDeadline = deadline;
538     if (durationNs >= 0) {
539         const auto timeoutDurationDeadline = makeDeadline(durationNs);
540         if (!closestDeadline.has_value() || *closestDeadline > timeoutDurationDeadline) {
541             closestDeadline = timeoutDurationDeadline;
542         }
543     }
544 
545     TimePoint driverStartAfterFence;
546     if (measureTiming) driverStartAfterFence = Clock::now();
547 
548     NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INPUTS_AND_OUTPUTS,
549                         "SamplePreparedModel::executeFenced");
550     const auto [poolStatus, requestPoolInfos, bufferWrappers] =
551             createRunTimePoolInfos(request, *mDriver, this);
552     if (poolStatus != aidl_hal::ErrorStatus::NONE) {
553         return toAStatus(poolStatus);
554     }
555 
556     NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
557                         "SamplePreparedModel::executeFenced");
558     CpuExecutor executor = mDriver->getExecutor();
559     if (loopTimeoutDurationNs >= 0) {
560         executor.setLoopTimeout(loopTimeoutDurationNs);
561     }
562     if (closestDeadline.has_value()) {
563         executor.setDeadline(*closestDeadline);
564     }
565     if (measureTiming) deviceStart = Clock::now();
566     int n = executor.run(model, request, mPoolInfos, requestPoolInfos);
567     if (measureTiming) deviceEnd = Clock::now();
568     VLOG(DRIVER) << "executor.run returned " << n;
569     aidl_hal::ErrorStatus executionStatus = convertResultCodeToAidlErrorStatus(n);
570     if (executionStatus != aidl_hal::ErrorStatus::NONE) {
571         return toAStatus(executionStatus);
572     }
573 
574     // Set output memories to the initialized state.
575     if (executionStatus == aidl_hal::ErrorStatus::NONE) {
576         for (const auto& output : request.outputs) {
577             const uint32_t poolIndex = output.location.poolIndex;
578             const auto& pool = request.pools[poolIndex];
579             if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
580                 bufferWrappers[poolIndex]->setInitialized(true);
581             }
582         }
583     }
584 
585     aidl_hal::Timing timingSinceLaunch = kNoTiming;
586     aidl_hal::Timing timingAfterFence = kNoTiming;
587     if (measureTiming) {
588         driverEnd = Clock::now();
589         timingSinceLaunch = {.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
590                              .timeInDriverNs = nanosecondsDuration(driverEnd, driverStart)};
591         timingAfterFence = {
592                 .timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
593                 .timeInDriverNs = nanosecondsDuration(driverEnd, driverStartAfterFence)};
594         VLOG(DRIVER) << "executeFenced timingSinceLaunch = " << timingSinceLaunch.toString();
595         VLOG(DRIVER) << "executeFenced timingAfterFence = " << timingAfterFence.toString();
596     }
597 
598     executionResult->callback = ndk::SharedRefBase::make<SampleFencedExecutionCallback>(
599             timingSinceLaunch, timingAfterFence, executionStatus);
600     executionResult->syncFence = ndk::ScopedFileDescriptor();
601     return ndk::ScopedAStatus::ok();
602 }
603 
executeSynchronouslyWithConfig(const aidl_hal::Request & request,const aidl_hal::ExecutionConfig & config,int64_t deadlineNs,aidl_hal::ExecutionResult * executionResult)604 ndk::ScopedAStatus SamplePreparedModel::executeSynchronouslyWithConfig(
605         const aidl_hal::Request& request, const aidl_hal::ExecutionConfig& config,
606         int64_t deadlineNs, aidl_hal::ExecutionResult* executionResult) {
607     return executeSynchronously(request, config.measureTiming, deadlineNs,
608                                 config.loopTimeoutDurationNs, executionResult);
609 }
executeFencedWithConfig(const aidl_hal::Request & request,const std::vector<ndk::ScopedFileDescriptor> & waitFor,const aidl_hal::ExecutionConfig & config,int64_t deadlineNs,int64_t durationNs,aidl_hal::FencedExecutionResult * executionResult)610 ndk::ScopedAStatus SamplePreparedModel::executeFencedWithConfig(
611         const aidl_hal::Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor,
612         const aidl_hal::ExecutionConfig& config, int64_t deadlineNs, int64_t durationNs,
613         aidl_hal::FencedExecutionResult* executionResult) {
614     return executeFenced(request, waitFor, config.measureTiming, deadlineNs,
615                          config.loopTimeoutDurationNs, durationNs, executionResult);
616 }
617 
configureExecutionBurst(std::shared_ptr<aidl_hal::IBurst> * burst)618 ndk::ScopedAStatus SamplePreparedModel::configureExecutionBurst(
619         std::shared_ptr<aidl_hal::IBurst>* burst) {
620     std::shared_ptr<SamplePreparedModel> self = this->template ref<SamplePreparedModel>();
621     *burst = ndk::SharedRefBase::make<SampleBurst>(std::move(self));
622     return ndk::ScopedAStatus::ok();
623 }
624 
SampleBurst(std::shared_ptr<SamplePreparedModel> preparedModel)625 SampleBurst::SampleBurst(std::shared_ptr<SamplePreparedModel> preparedModel)
626     : kPreparedModel(std::move(preparedModel)) {
627     CHECK(kPreparedModel != nullptr);
628 }
629 
executeSynchronously(const aidl_hal::Request & request,const std::vector<int64_t> & memoryIdentifierTokens,bool measureTiming,int64_t deadlineNs,int64_t loopTimeoutDurationNs,aidl_hal::ExecutionResult * executionResult)630 ndk::ScopedAStatus SampleBurst::executeSynchronously(
631         const aidl_hal::Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
632         bool measureTiming, int64_t deadlineNs, int64_t loopTimeoutDurationNs,
633         aidl_hal::ExecutionResult* executionResult) {
634     if (request.pools.size() != memoryIdentifierTokens.size()) {
635         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
636                          "request.pools.size() != memoryIdentifierTokens.size()");
637     }
638     if (!std::all_of(memoryIdentifierTokens.begin(), memoryIdentifierTokens.end(),
639                      [](int64_t token) { return token >= -1; })) {
640         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid memoryIdentifierTokens");
641     }
642 
643     // Ensure at most one execution is in flight at a time.
644     const bool executionAlreadyInFlight = mExecutionInFlight.test_and_set();
645     if (executionAlreadyInFlight) {
646         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
647                          "Burst object supports at most one execution at a time");
648     }
649     const auto guard = base::make_scope_guard([this] { mExecutionInFlight.clear(); });
650 
651     return kPreparedModel->executeSynchronously(request, measureTiming, deadlineNs,
652                                                 loopTimeoutDurationNs, executionResult);
653 }
654 
executeSynchronouslyWithConfig(const aidl_hal::Request & request,const std::vector<int64_t> & memoryIdentifierTokens,const aidl_hal::ExecutionConfig & config,int64_t deadlineNs,aidl_hal::ExecutionResult * executionResult)655 ndk::ScopedAStatus SampleBurst::executeSynchronouslyWithConfig(
656         const aidl_hal::Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
657         const aidl_hal::ExecutionConfig& config, int64_t deadlineNs,
658         aidl_hal::ExecutionResult* executionResult) {
659     return executeSynchronously(request, memoryIdentifierTokens, config.measureTiming, deadlineNs,
660                                 config.loopTimeoutDurationNs, executionResult);
661 }
662 
releaseMemoryResource(int64_t memoryIdentifierToken)663 ndk::ScopedAStatus SampleBurst::releaseMemoryResource(int64_t memoryIdentifierToken) {
664     if (memoryIdentifierToken < -1) {
665         return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid memoryIdentifierToken");
666     }
667     return ndk::ScopedAStatus::ok();
668 }
669 
createReusableExecution(const aidl_hal::Request & halRequest,const aidl_hal::ExecutionConfig & config,std::shared_ptr<aidl_hal::IExecution> * execution)670 ndk::ScopedAStatus SamplePreparedModel::createReusableExecution(
671         const aidl_hal::Request& halRequest, const aidl_hal::ExecutionConfig& config,
672         std::shared_ptr<aidl_hal::IExecution>* execution) {
673     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
674                  "SamplePreparedModel::createReusableExecution");
675     VLOG(DRIVER) << "SamplePreparedModel::createReusableExecution";
676 
677     auto maybeClonedRequest = aidl_hal::utils::clone(halRequest);
678     if (!maybeClonedRequest.ok()) {
679         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
680                          maybeClonedRequest.error().message);
681     }
682 
683     std::shared_ptr<SamplePreparedModel> self = this->template ref<SamplePreparedModel>();
684     *execution = ndk::SharedRefBase::make<SampleExecution>(
685             std::move(self), std::move(maybeClonedRequest).value(), config.measureTiming,
686             config.loopTimeoutDurationNs);
687     return ndk::ScopedAStatus::ok();
688 }
689 
executeSynchronously(int64_t halDeadlineNs,aidl_hal::ExecutionResult * executionResult)690 ndk::ScopedAStatus SampleExecution::executeSynchronously(
691         int64_t halDeadlineNs, aidl_hal::ExecutionResult* executionResult) {
692     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
693                  "SampleExecution::executeSynchronously");
694     VLOG(DRIVER) << "SampleExecution::executeSynchronously";
695 
696     // Ensure at most one computation is in flight at a time.
697     const bool computationAlreadyInFlight = mComputationInFlight.test_and_set();
698     if (computationAlreadyInFlight) {
699         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
700                          "Execution object supports at most one computation at a time");
701     }
702     const auto guard = base::make_scope_guard([this] { mComputationInFlight.clear(); });
703 
704     return kPreparedModel->executeSynchronously(kHalRequest, kMeasureTiming, halDeadlineNs,
705                                                 kLoopTimeoutDurationNs, executionResult);
706 }
707 
executeFenced(const std::vector<ndk::ScopedFileDescriptor> & waitFor,int64_t deadlineNs,int64_t durationNs,aidl_hal::FencedExecutionResult * executionResult)708 ndk::ScopedAStatus SampleExecution::executeFenced(
709         const std::vector<ndk::ScopedFileDescriptor>& waitFor, int64_t deadlineNs,
710         int64_t durationNs, aidl_hal::FencedExecutionResult* executionResult) {
711     NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION, "SampleExecution::executeFenced");
712     VLOG(DRIVER) << "SampleExecution::executeFenced";
713 
714     // Ensure at most one computation is in flight at a time.
715     const bool computationAlreadyInFlight = mComputationInFlight.test_and_set();
716     if (computationAlreadyInFlight) {
717         return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
718                          "Execution object supports at most one computation at a time");
719     }
720     const auto guard = base::make_scope_guard([this] { mComputationInFlight.clear(); });
721 
722     return kPreparedModel->executeFenced(kHalRequest, waitFor, kMeasureTiming, deadlineNs,
723                                          kLoopTimeoutDurationNs, durationNs, executionResult);
724 }
725 
726 }  // namespace sample_driver_aidl
727 }  // namespace nn
728 }  // namespace android
729