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