Cleanup NN callback error handling

This CL introduces a new templated class CallbackValue to handle HIDL
"return value" callbacks in a terser and more readable way.

This CL also introduces a new macro HANDLE_HAL_STATUS to return from the
current function when an error is present with the ability to append a
more descriptive error message.

Finally, this CL changes the behavior of synchronous executions. Prior
to this CL, IPreparedModel fell back to an asynchronous execution if the
synchronous execution was allowed and failed. This change instead
returns a failure if synchronous execution is allowed and fails.

Bug: 173084343
Test: mma
Change-Id: I62714a932e71dfc77401bbcb9eaaaf3d94fb9707
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
index 6056498..d710b85 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -36,6 +36,7 @@
 #include <nnapi/hal/1.1/Conversions.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/1.2/Device.h>
+#include <nnapi/hal/1.2/Utils.h>
 #include <nnapi/hal/CommonUtils.h>
 #include <nnapi/hal/HandleError.h>
 #include <nnapi/hal/ProtectCallback.h>
@@ -69,29 +70,27 @@
     return hidlPreparedModels;
 }
 
-nn::GeneralResult<nn::SharedBuffer> convert(
-        nn::GeneralResult<std::shared_ptr<const Buffer>> result) {
-    return NN_TRY(std::move(result));
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
+                                                         const Capabilities& capabilities) {
+    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    return nn::convert(capabilities);
 }
 
-nn::GeneralResult<nn::Capabilities> initCapabilities(V1_3::IDevice* device) {
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_3::IDevice* device) {
     CHECK(device != nullptr);
 
-    nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                                                 << "uninitialized";
-    const auto cb = [&result](ErrorStatus status, const Capabilities& capabilities) {
-        if (status != ErrorStatus::NONE) {
-            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
-            result = NN_ERROR(canonical) << "getCapabilities_1_3 failed with " << toString(status);
-        } else {
-            result = nn::convert(capabilities);
-        }
-    };
+    auto cb = hal::utils::CallbackValue(capabilitiesCallback);
 
     const auto ret = device->getCapabilities_1_3(cb);
     HANDLE_TRANSPORT_FAILURE(ret);
 
-    return result;
+    return cb.take();
+}
+
+nn::GeneralResult<nn::SharedBuffer> allocationCallback(ErrorStatus status,
+                                                       const sp<IBuffer>& buffer, uint32_t token) {
+    HANDLE_HAL_STATUS(status) << "IDevice::allocate failed with " << toString(status);
+    return Buffer::create(buffer, static_cast<nn::Request::MemoryDomainToken>(token));
 }
 
 }  // namespace
@@ -107,12 +106,12 @@
                << "V1_3::utils::Device::create must have non-null device";
     }
 
-    auto versionString = NN_TRY(V1_2::utils::initVersionString(device.get()));
-    const auto deviceType = NN_TRY(V1_2::utils::initDeviceType(device.get()));
-    auto extensions = NN_TRY(V1_2::utils::initExtensions(device.get()));
-    auto capabilities = NN_TRY(initCapabilities(device.get()));
+    auto versionString = NN_TRY(V1_2::utils::getVersionStringFrom(device.get()));
+    const auto deviceType = NN_TRY(V1_2::utils::getDeviceTypeFrom(device.get()));
+    auto extensions = NN_TRY(V1_2::utils::getSupportedExtensionsFrom(device.get()));
+    auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
     const auto numberOfCacheFilesNeeded =
-            NN_TRY(V1_2::utils::initNumberOfCacheFilesNeeded(device.get()));
+            NN_TRY(V1_2::utils::getNumberOfCacheFilesNeededFrom(device.get()));
 
     auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
     return std::make_shared<const Device>(
@@ -177,27 +176,12 @@
 
     const auto hidlModel = NN_TRY(convert(modelInShared));
 
-    nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                                                  << "uninitialized";
-    auto cb = [&result, &model](ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
-        if (status != ErrorStatus::NONE) {
-            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
-            result = NN_ERROR(canonical)
-                     << "IDevice::getSupportedOperations_1_3 failed with " << toString(status);
-        } else if (supportedOperations.size() != model.main.operations.size()) {
-            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                     << "IDevice::getSupportedOperations_1_3 returned vector of size "
-                     << supportedOperations.size() << " but expected "
-                     << model.main.operations.size();
-        } else {
-            result = supportedOperations;
-        }
-    };
+    auto cb = hal::utils::CallbackValue(supportedOperationsCallback);
 
     const auto ret = kDevice->getSupportedOperations_1_3(hidlModel, cb);
     HANDLE_TRANSPORT_FAILURE(ret);
 
-    return result;
+    return cb.take();
 }
 
 nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
@@ -210,12 +194,12 @@
             NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
 
     const auto hidlModel = NN_TRY(convert(modelInShared));
-    const auto hidlPreference = NN_TRY(V1_1::utils::convert(preference));
+    const auto hidlPreference = NN_TRY(convert(preference));
     const auto hidlPriority = NN_TRY(convert(priority));
     const auto hidlDeadline = NN_TRY(convert(deadline));
-    const auto hidlModelCache = NN_TRY(V1_2::utils::convert(modelCache));
-    const auto hidlDataCache = NN_TRY(V1_2::utils::convert(dataCache));
-    const auto hidlToken = token;
+    const auto hidlModelCache = NN_TRY(convert(modelCache));
+    const auto hidlDataCache = NN_TRY(convert(dataCache));
+    const auto hidlToken = V1_2::utils::CacheToken{token};
 
     const auto cb = sp<PreparedModelCallback>::make();
     const auto scoped = kDeathHandler.protectCallback(cb.get());
@@ -224,10 +208,7 @@
             kDevice->prepareModel_1_3(hidlModel, hidlPreference, hidlPriority, hidlDeadline,
                                       hidlModelCache, hidlDataCache, hidlToken, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    if (status != ErrorStatus::NONE) {
-        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
-        return NN_ERROR(canonical) << "prepareModel_1_3 failed with " << toString(status);
-    }
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
 
     return cb->get();
 }
@@ -236,9 +217,9 @@
         nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
         const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
     const auto hidlDeadline = NN_TRY(convert(deadline));
-    const auto hidlModelCache = NN_TRY(V1_2::utils::convert(modelCache));
-    const auto hidlDataCache = NN_TRY(V1_2::utils::convert(dataCache));
-    const auto hidlToken = token;
+    const auto hidlModelCache = NN_TRY(convert(modelCache));
+    const auto hidlDataCache = NN_TRY(convert(dataCache));
+    const auto hidlToken = V1_2::utils::CacheToken{token};
 
     const auto cb = sp<PreparedModelCallback>::make();
     const auto scoped = kDeathHandler.protectCallback(cb.get());
@@ -246,10 +227,7 @@
     const auto ret = kDevice->prepareModelFromCache_1_3(hidlDeadline, hidlModelCache, hidlDataCache,
                                                         hidlToken, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    if (status != ErrorStatus::NONE) {
-        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
-        return NN_ERROR(canonical) << "prepareModelFromCache_1_3 failed with " << toString(status);
-    }
+    HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
 
     return cb->get();
 }
@@ -263,27 +241,13 @@
     const auto hidlInputRoles = NN_TRY(convert(inputRoles));
     const auto hidlOutputRoles = NN_TRY(convert(outputRoles));
 
-    nn::GeneralResult<nn::SharedBuffer> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                                                 << "uninitialized";
-    auto cb = [&result](ErrorStatus status, const sp<IBuffer>& buffer, uint32_t token) {
-        if (status != ErrorStatus::NONE) {
-            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
-            result = NN_ERROR(canonical) << "IDevice::allocate failed with " << toString(status);
-        } else if (buffer == nullptr) {
-            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Returned buffer is nullptr";
-        } else if (token == 0) {
-            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Returned token is invalid (0)";
-        } else {
-            result = convert(
-                    Buffer::create(buffer, static_cast<nn::Request::MemoryDomainToken>(token)));
-        }
-    };
+    auto cb = hal::utils::CallbackValue(allocationCallback);
 
     const auto ret =
             kDevice->allocate(hidlDesc, hidlPreparedModels, hidlInputRoles, hidlOutputRoles, cb);
     HANDLE_TRANSPORT_FAILURE(ret);
 
-    return result;
+    return cb.take();
 }
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils