Implement NNAPI canonical interfaces

This CL implements the canonical IDevice, IPreparedModel, and IBuffer
interfaces for the 1.0, 1.1, 1.2, and 1.3 NN HIDL HAL interfaces.
Further, it introduces "Resilient" adapter interfaces to automatically
retrieve a handle to a recovered interface object after it has died and
rebooted.

This CL also updates the conversion code from returning nn::Result to
nn::GeneralResult, which includes a ErrorStatus code in the case of an
error.

Finally, this CL introduces a new static library
neuralnetworks_utils_hal_service which consists of a single function
::android::nn::hal::getDevices which can be used by the NNAPI runtime to
retrieve the HIDL services without knowing the underlying HIDL types.

Bug: 160668438
Test: mma
Test: NeuralNetworksTest_static
Change-Id: Iec6ae739df196b4034ffb35ea76781fd541ffec3
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index b61dc97..21562cf 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -20,6 +20,7 @@
     srcs: ["src/*"],
     local_include_dirs: ["include/nnapi/hal"],
     export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
     static_libs: [
         "neuralnetworks_types",
     ],
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 8c01368..254a3d4 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -19,6 +19,7 @@
 
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <functional>
 #include <vector>
 
 // Shorthand
@@ -42,14 +43,16 @@
 bool hasNoPointerData(const nn::Request& request);
 
 // Relocate pointer-based data to shared memory.
-nn::Result<nn::Model> flushDataFromPointerToShared(const nn::Model& model);
-nn::Result<nn::Request> flushDataFromPointerToShared(const nn::Request& request);
+nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
+        const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut);
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
+        const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut);
 
 // Undoes `flushDataFromPointerToShared` on a Request object. More specifically,
 // `unflushDataFromSharedToPointer` copies the output shared memory data from the transformed
 // Request object back to the output pointer-based memory in the original Request object.
-nn::Result<void> unflushDataFromSharedToPointer(const nn::Request& request,
-                                                const nn::Request& requestInShared);
+nn::GeneralResult<void> unflushDataFromSharedToPointer(
+        const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared);
 
 std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
                                              const std::vector<nn::Operation>& operations);
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
new file mode 100644
index 0000000..e4046b5
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+namespace android::hardware::neuralnetworks::utils {
+
+template <typename Type>
+nn::GeneralResult<Type> handleTransportError(const hardware::Return<Type>& ret) {
+    if (ret.isDeadObject()) {
+        return NN_ERROR(nn::ErrorStatus::DEAD_OBJECT)
+               << "Return<>::isDeadObject returned true: " << ret.description();
+    }
+    if (!ret.isOk()) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Return<>::isOk returned false: " << ret.description();
+    }
+    return ret;
+}
+
+template <>
+inline nn::GeneralResult<void> handleTransportError(const hardware::Return<void>& ret) {
+    if (ret.isDeadObject()) {
+        return NN_ERROR(nn::ErrorStatus::DEAD_OBJECT)
+               << "Return<>::isDeadObject returned true: " << ret.description();
+    }
+    if (!ret.isOk()) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Return<>::isOk returned false: " << ret.description();
+    }
+    return {};
+}
+
+template <typename Type>
+nn::GeneralResult<Type> makeGeneralFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+    if (!result.has_value()) {
+        return nn::error(status) << std::move(result).error();
+    }
+    return std::move(result).value();
+}
+
+template <>
+inline nn::GeneralResult<void> makeGeneralFailure(nn::Result<void> result, nn::ErrorStatus status) {
+    if (!result.has_value()) {
+        return nn::error(status) << std::move(result).error();
+    }
+    return {};
+}
+
+template <typename Type>
+nn::ExecutionResult<Type> makeExecutionFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+    if (!result.has_value()) {
+        return nn::error(status) << std::move(result).error();
+    }
+    return std::move(result).value();
+}
+
+template <>
+inline nn::ExecutionResult<void> makeExecutionFailure(nn::Result<void> result,
+                                                      nn::ErrorStatus status) {
+    if (!result.has_value()) {
+        return nn::error(status) << std::move(result).error();
+    }
+    return {};
+}
+
+template <typename Type>
+nn::ExecutionResult<Type> makeExecutionFailure(nn::GeneralResult<Type> result) {
+    if (!result.has_value()) {
+        const auto [message, status] = std::move(result).error();
+        return nn::error(status) << message;
+    }
+    return std::move(result).value();
+}
+
+template <>
+inline nn::ExecutionResult<void> makeExecutionFailure(nn::GeneralResult<void> result) {
+    if (!result.has_value()) {
+        const auto [message, status] = std::move(result).error();
+        return nn::error(status) << message;
+    }
+    return {};
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
\ No newline at end of file
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
new file mode 100644
index 0000000..85bd613
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
+
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <android/hidl/base/1.0/IBase.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <mutex>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class IProtectedCallback {
+  public:
+    /**
+     * Marks this object as a dead object.
+     */
+    virtual void notifyAsDeadObject() = 0;
+
+    // Public virtual destructor to allow objects to be stored (and destroyed) as smart pointers.
+    // E.g., std::unique_ptr<IProtectedCallback>.
+    virtual ~IProtectedCallback() = default;
+
+  protected:
+    // Protect the non-destructor special member functions to prevent object slicing.
+    IProtectedCallback() = default;
+    IProtectedCallback(const IProtectedCallback&) = default;
+    IProtectedCallback(IProtectedCallback&&) noexcept = default;
+    IProtectedCallback& operator=(const IProtectedCallback&) = default;
+    IProtectedCallback& operator=(IProtectedCallback&&) noexcept = default;
+};
+
+// Thread safe class
+class DeathRecipient final : public hidl_death_recipient {
+  public:
+    void serviceDied(uint64_t /*cookie*/, const wp<hidl::base::V1_0::IBase>& /*who*/) override;
+    // Precondition: `killable` must be non-null.
+    void add(IProtectedCallback* killable) const;
+    // Precondition: `killable` must be non-null.
+    void remove(IProtectedCallback* killable) const;
+
+  private:
+    mutable std::mutex mMutex;
+    mutable std::vector<IProtectedCallback*> mObjects GUARDED_BY(mMutex);
+};
+
+class DeathHandler final {
+  public:
+    static nn::GeneralResult<DeathHandler> create(sp<hidl::base::V1_0::IBase> object);
+
+    DeathHandler(const DeathHandler&) = delete;
+    DeathHandler(DeathHandler&&) noexcept = default;
+    DeathHandler& operator=(const DeathHandler&) = delete;
+    DeathHandler& operator=(DeathHandler&&) noexcept = delete;
+    ~DeathHandler();
+
+    using Cleanup = std::function<void()>;
+    // Precondition: `killable` must be non-null.
+    [[nodiscard]] base::ScopeGuard<Cleanup> protectCallback(IProtectedCallback* killable) const;
+
+  private:
+    DeathHandler(sp<hidl::base::V1_0::IBase> object, sp<DeathRecipient> deathRecipient);
+
+    sp<hidl::base::V1_0::IBase> kObject;
+    sp<DeathRecipient> kDeathRecipient;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
new file mode 100644
index 0000000..996ec1e
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BUFFER_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientBuffer final : public nn::IBuffer {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedBuffer>(bool blocking)>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientBuffer>> create(Factory makeBuffer);
+
+    explicit ResilientBuffer(PrivateConstructorTag tag, Factory makeBuffer,
+                             nn::SharedBuffer buffer);
+
+    nn::SharedBuffer getBuffer() const;
+    nn::SharedBuffer recover(const nn::IBuffer* failingBuffer, bool blocking) const;
+
+    nn::Request::MemoryDomainToken getToken() const override;
+
+    nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+
+    nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+                                     const nn::Dimensions& dimensions) const override;
+
+  private:
+    const Factory kMakeBuffer;
+    mutable std::mutex mMutex;
+    mutable nn::SharedBuffer mBuffer GUARDED_BY(mMutex);
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BUFFER_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
new file mode 100644
index 0000000..4f1afb9
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_DEVICE_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientDevice final : public nn::IDevice,
+                              public std::enable_shared_from_this<ResilientDevice> {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedDevice>(bool blocking)>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientDevice>> create(Factory makeDevice);
+
+    explicit ResilientDevice(PrivateConstructorTag tag, Factory makeDevice, std::string name,
+                             std::string versionString, std::vector<nn::Extension> extensions,
+                             nn::Capabilities capabilities, nn::SharedDevice device);
+
+    nn::SharedDevice getDevice() const;
+    nn::SharedDevice recover(const nn::IDevice* failingDevice, bool blocking) const;
+
+    const std::string& getName() const override;
+    const std::string& getVersionString() const override;
+    nn::Version getFeatureLevel() const override;
+    nn::DeviceType getType() const override;
+    const std::vector<nn::Extension>& getSupportedExtensions() const override;
+    const nn::Capabilities& getCapabilities() const override;
+    std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+    nn::GeneralResult<void> wait() const override;
+
+    nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+            const nn::Model& model) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+            const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+            nn::OptionalTimePoint deadline, const std::vector<nn::NativeHandle>& modelCache,
+            const std::vector<nn::NativeHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::NativeHandle>& modelCache,
+            const std::vector<nn::NativeHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedBuffer> allocate(
+            const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const override;
+
+  private:
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelInternal(
+            bool blocking, const nn::Model& model, nn::ExecutionPreference preference,
+            nn::Priority priority, nn::OptionalTimePoint deadline,
+            const std::vector<nn::NativeHandle>& modelCache,
+            const std::vector<nn::NativeHandle>& dataCache, const nn::CacheToken& token) const;
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCacheInternal(
+            bool blocking, nn::OptionalTimePoint deadline,
+            const std::vector<nn::NativeHandle>& modelCache,
+            const std::vector<nn::NativeHandle>& dataCache, const nn::CacheToken& token) const;
+    nn::GeneralResult<nn::SharedBuffer> allocateInternal(
+            bool blocking, const nn::BufferDesc& desc,
+            const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const;
+
+    const Factory kMakeDevice;
+    const std::string kName;
+    const std::string kVersionString;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    mutable std::mutex mMutex;
+    mutable nn::SharedDevice mDevice GUARDED_BY(mMutex);
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_DEVICE_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
new file mode 100644
index 0000000..c2940d1
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_PREPARED_MODEL_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientPreparedModel final : public nn::IPreparedModel {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedPreparedModel>(bool blocking)>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientPreparedModel>> create(
+            Factory makePreparedModel);
+
+    explicit ResilientPreparedModel(PrivateConstructorTag tag, Factory makePreparedModel,
+                                    nn::SharedPreparedModel preparedModel);
+
+    nn::SharedPreparedModel getPreparedModel() const;
+    nn::SharedPreparedModel recover(const nn::IPreparedModel* failingPreparedModel,
+                                    bool blocking) const;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced(
+            const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+            nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+            const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+
+    std::any getUnderlyingResource() const override;
+
+  private:
+    const Factory kMakePreparedModel;
+    mutable std::mutex mMutex;
+    mutable nn::SharedPreparedModel mPreparedModel GUARDED_BY(mMutex);
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_PREPARED_MODEL_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h b/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h
new file mode 100644
index 0000000..7103c6b
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H
+
+#include <android-base/thread_annotations.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <optional>
+
+namespace android::hardware::neuralnetworks::utils {
+
+// This class is thread safe.
+template <typename Type>
+class TransferValue final {
+  public:
+    void put(Type object) const;
+    [[nodiscard]] Type take() const;
+
+  private:
+    mutable std::mutex mMutex;
+    mutable std::condition_variable mCondition;
+    mutable std::optional<Type> mObject GUARDED_BY(mMutex);
+};
+
+// template implementation
+
+template <typename Type>
+void TransferValue<Type>::put(Type object) const {
+    {
+        std::lock_guard guard(mMutex);
+        // Immediately return if value already exists.
+        if (mObject.has_value()) return;
+        mObject.emplace(std::move(object));
+    }
+    mCondition.notify_all();
+}
+
+template <typename Type>
+Type TransferValue<Type>::take() const {
+    std::unique_lock lock(mMutex);
+    base::ScopedLockAssertion lockAssertion(mMutex);
+    mCondition.wait(lock, [this]() REQUIRES(mMutex) { return mObject.has_value(); });
+    std::optional<Type> object;
+    std::swap(object, mObject);
+    return std::move(object).value();
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index 667189b..2565972 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -16,6 +16,8 @@
 
 #include "CommonUtils.h"
 
+#include "HandleError.h"
+
 #include <android-base/logging.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
@@ -25,6 +27,7 @@
 
 #include <algorithm>
 #include <any>
+#include <functional>
 #include <optional>
 #include <variant>
 #include <vector>
@@ -111,8 +114,18 @@
     return hasNoPointerData(request.inputs) && hasNoPointerData(request.outputs);
 }
 
-nn::Result<nn::Model> flushDataFromPointerToShared(const nn::Model& model) {
-    auto modelInShared = model;
+nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
+        const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut) {
+    CHECK(model != nullptr);
+    CHECK(maybeModelInSharedOut != nullptr);
+
+    if (hasNoPointerData(*model)) {
+        return *model;
+    }
+
+    // Make a copy of the model in order to make modifications. The modified model is returned to
+    // the caller through `maybeModelInSharedOut` if the function succeeds.
+    nn::Model modelInShared = *model;
 
     nn::ConstantMemoryBuilder memoryBuilder(modelInShared.pools.size());
     copyPointersToSharedMemory(&modelInShared.main, &memoryBuilder);
@@ -126,11 +139,22 @@
         modelInShared.pools.push_back(std::move(memory));
     }
 
-    return modelInShared;
+    *maybeModelInSharedOut = modelInShared;
+    return **maybeModelInSharedOut;
 }
 
-nn::Result<nn::Request> flushDataFromPointerToShared(const nn::Request& request) {
-    auto requestInShared = request;
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
+        const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut) {
+    CHECK(request != nullptr);
+    CHECK(maybeRequestInSharedOut != nullptr);
+
+    if (hasNoPointerData(*request)) {
+        return *request;
+    }
+
+    // Make a copy of the request in order to make modifications. The modified request is returned
+    // to the caller through `maybeRequestInSharedOut` if the function succeeds.
+    nn::Request requestInShared = *request;
 
     // Change input pointers to shared memory.
     nn::ConstantMemoryBuilder inputBuilder(requestInShared.pools.size());
@@ -171,15 +195,17 @@
         requestInShared.pools.push_back(std::move(memory));
     }
 
-    return requestInShared;
+    *maybeRequestInSharedOut = requestInShared;
+    return **maybeRequestInSharedOut;
 }
 
-nn::Result<void> unflushDataFromSharedToPointer(const nn::Request& request,
-                                                const nn::Request& requestInShared) {
-    if (requestInShared.pools.empty() ||
-        !std::holds_alternative<nn::Memory>(requestInShared.pools.back())) {
+nn::GeneralResult<void> unflushDataFromSharedToPointer(
+        const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared) {
+    if (!maybeRequestInShared.has_value() || maybeRequestInShared->pools.empty() ||
+        !std::holds_alternative<nn::Memory>(maybeRequestInShared->pools.back())) {
         return {};
     }
+    const auto& requestInShared = *maybeRequestInShared;
 
     // Map the memory.
     const auto& outputMemory = std::get<nn::Memory>(requestInShared.pools.back());
diff --git a/neuralnetworks/utils/common/src/ProtectCallback.cpp b/neuralnetworks/utils/common/src/ProtectCallback.cpp
new file mode 100644
index 0000000..1d9a307
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ProtectCallback.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProtectCallback.h"
+
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <android/hidl/base/1.0/IBase.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <algorithm>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+void DeathRecipient::serviceDied(uint64_t /*cookie*/, const wp<hidl::base::V1_0::IBase>& /*who*/) {
+    std::lock_guard guard(mMutex);
+    std::for_each(mObjects.begin(), mObjects.end(),
+                  [](IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
+}
+
+void DeathRecipient::add(IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    std::lock_guard guard(mMutex);
+    mObjects.push_back(killable);
+}
+
+void DeathRecipient::remove(IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    std::lock_guard guard(mMutex);
+    const auto removedIter = std::remove(mObjects.begin(), mObjects.end(), killable);
+    mObjects.erase(removedIter);
+}
+
+nn::GeneralResult<DeathHandler> DeathHandler::create(sp<hidl::base::V1_0::IBase> object) {
+    if (object == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::DeathHandler::create must have non-null object";
+    }
+    auto deathRecipient = sp<DeathRecipient>::make();
+
+    const auto ret = object->linkToDeath(deathRecipient, /*cookie=*/0);
+    const bool success = NN_TRY(handleTransportError(ret));
+    if (!success) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "IBase::linkToDeath returned false";
+    }
+
+    return DeathHandler(std::move(object), std::move(deathRecipient));
+}
+
+DeathHandler::DeathHandler(sp<hidl::base::V1_0::IBase> object, sp<DeathRecipient> deathRecipient)
+    : kObject(std::move(object)), kDeathRecipient(std::move(deathRecipient)) {
+    CHECK(kObject != nullptr);
+    CHECK(kDeathRecipient != nullptr);
+}
+
+DeathHandler::~DeathHandler() {
+    if (kObject != nullptr && kDeathRecipient != nullptr) {
+        const auto ret = kObject->unlinkToDeath(kDeathRecipient);
+        const auto maybeSuccess = handleTransportError(ret);
+        if (!maybeSuccess.has_value()) {
+            LOG(ERROR) << maybeSuccess.error().message;
+        } else if (!maybeSuccess.value()) {
+            LOG(ERROR) << "IBase::linkToDeath returned false";
+        }
+    }
+}
+
+[[nodiscard]] base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
+        IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    kDeathRecipient->add(killable);
+    return base::make_scope_guard(
+            [deathRecipient = kDeathRecipient, killable] { deathRecipient->remove(killable); });
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientBuffer.cpp b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
new file mode 100644
index 0000000..984295b
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResilientBuffer.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const ResilientBuffer>> ResilientBuffer::create(
+        Factory makeBuffer) {
+    if (makeBuffer == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientBuffer::create must have non-empty makeBuffer";
+    }
+    auto buffer = NN_TRY(makeBuffer(/*blocking=*/true));
+    CHECK(buffer != nullptr);
+    return std::make_shared<const ResilientBuffer>(PrivateConstructorTag{}, std::move(makeBuffer),
+                                                   std::move(buffer));
+}
+
+ResilientBuffer::ResilientBuffer(PrivateConstructorTag /*tag*/, Factory makeBuffer,
+                                 nn::SharedBuffer buffer)
+    : kMakeBuffer(std::move(makeBuffer)), mBuffer(std::move(buffer)) {
+    CHECK(kMakeBuffer != nullptr);
+    CHECK(mBuffer != nullptr);
+}
+
+nn::SharedBuffer ResilientBuffer::getBuffer() const {
+    std::lock_guard guard(mMutex);
+    return mBuffer;
+}
+nn::SharedBuffer ResilientBuffer::recover(const nn::IBuffer* /*failingBuffer*/,
+                                          bool /*blocking*/) const {
+    std::lock_guard guard(mMutex);
+    return mBuffer;
+}
+
+nn::Request::MemoryDomainToken ResilientBuffer::getToken() const {
+    return getBuffer()->getToken();
+}
+
+nn::GeneralResult<void> ResilientBuffer::copyTo(const nn::Memory& dst) const {
+    return getBuffer()->copyTo(dst);
+}
+
+nn::GeneralResult<void> ResilientBuffer::copyFrom(const nn::Memory& src,
+                                                  const nn::Dimensions& dimensions) const {
+    return getBuffer()->copyFrom(src, dimensions);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientDevice.cpp b/neuralnetworks/utils/common/src/ResilientDevice.cpp
new file mode 100644
index 0000000..95662d9
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResilientDevice.h"
+
+#include "ResilientBuffer.h"
+#include "ResilientPreparedModel.h"
+
+#include <android-base/logging.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientDevice& resilientDevice, const FnType& fn, bool blocking)
+        -> decltype(fn(*resilientDevice.getDevice())) {
+    auto device = resilientDevice.getDevice();
+    auto result = fn(*device);
+
+    // Immediately return if device is not dead.
+    if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+        return result;
+    }
+
+    device = resilientDevice.recover(device.get(), blocking);
+    return fn(*device);
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const ResilientDevice>> ResilientDevice::create(
+        Factory makeDevice) {
+    if (makeDevice == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientDevice::create must have non-empty makeDevice";
+    }
+    auto device = NN_TRY(makeDevice(/*blocking=*/true));
+    CHECK(device != nullptr);
+
+    auto name = device->getName();
+    auto versionString = device->getVersionString();
+    auto extensions = device->getSupportedExtensions();
+    auto capabilities = device->getCapabilities();
+
+    return std::make_shared<ResilientDevice>(PrivateConstructorTag{}, std::move(makeDevice),
+                                             std::move(name), std::move(versionString),
+                                             std::move(extensions), std::move(capabilities),
+                                             std::move(device));
+}
+
+ResilientDevice::ResilientDevice(PrivateConstructorTag /*tag*/, Factory makeDevice,
+                                 std::string name, std::string versionString,
+                                 std::vector<nn::Extension> extensions,
+                                 nn::Capabilities capabilities, nn::SharedDevice device)
+    : kMakeDevice(std::move(makeDevice)),
+      kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      mDevice(std::move(device)) {
+    CHECK(kMakeDevice != nullptr);
+    CHECK(mDevice != nullptr);
+}
+
+nn::SharedDevice ResilientDevice::getDevice() const {
+    std::lock_guard guard(mMutex);
+    return mDevice;
+}
+
+nn::SharedDevice ResilientDevice::recover(const nn::IDevice* failingDevice, bool blocking) const {
+    std::lock_guard guard(mMutex);
+
+    // Another caller updated the failing device.
+    if (mDevice.get() != failingDevice) {
+        return mDevice;
+    }
+
+    auto maybeDevice = kMakeDevice(blocking);
+    if (!maybeDevice.has_value()) {
+        const auto& [message, code] = maybeDevice.error();
+        LOG(ERROR) << "Failed to recover dead device with error " << code << ": " << message;
+        return mDevice;
+    }
+    auto device = std::move(maybeDevice).value();
+
+    // TODO(b/173081926): Instead of CHECKing to ensure the cache has not been changed, return an
+    // invalid/"null" IDevice object that always fails.
+    CHECK_EQ(kName, device->getName());
+    CHECK_EQ(kVersionString, device->getVersionString());
+    CHECK(kExtensions == device->getSupportedExtensions());
+    CHECK_EQ(kCapabilities, device->getCapabilities());
+
+    mDevice = std::move(device);
+    return mDevice;
+}
+
+const std::string& ResilientDevice::getName() const {
+    return kName;
+}
+
+const std::string& ResilientDevice::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version ResilientDevice::getFeatureLevel() const {
+    return getDevice()->getFeatureLevel();
+}
+
+nn::DeviceType ResilientDevice::getType() const {
+    return getDevice()->getType();
+}
+
+const std::vector<nn::Extension>& ResilientDevice::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& ResilientDevice::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> ResilientDevice::getNumberOfCacheFilesNeeded() const {
+    return getDevice()->getNumberOfCacheFilesNeeded();
+}
+
+nn::GeneralResult<void> ResilientDevice::wait() const {
+    const auto fn = [](const nn::IDevice& device) { return device.wait(); };
+    return protect(*this, fn, /*blocking=*/true);
+}
+
+nn::GeneralResult<std::vector<bool>> ResilientDevice::getSupportedOperations(
+        const nn::Model& model) const {
+    const auto fn = [&model](const nn::IDevice& device) {
+        return device.getSupportedOperations(model);
+    };
+    return protect(*this, fn, /*blocking=*/false);
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+        nn::OptionalTimePoint deadline, const std::vector<nn::NativeHandle>& modelCache,
+        const std::vector<nn::NativeHandle>& dataCache, const nn::CacheToken& token) const {
+    auto self = shared_from_this();
+    ResilientPreparedModel::Factory makePreparedModel =
+            [device = std::move(self), model, preference, priority, deadline, modelCache, dataCache,
+             token](bool blocking) -> nn::GeneralResult<nn::SharedPreparedModel> {
+        return device->prepareModelInternal(blocking, model, preference, priority, deadline,
+                                            modelCache, dataCache, token);
+    };
+    return ResilientPreparedModel::create(std::move(makePreparedModel));
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelFromCache(
+        nn::OptionalTimePoint deadline, const std::vector<nn::NativeHandle>& modelCache,
+        const std::vector<nn::NativeHandle>& dataCache, const nn::CacheToken& token) const {
+    auto self = shared_from_this();
+    ResilientPreparedModel::Factory makePreparedModel =
+            [device = std::move(self), deadline, modelCache, dataCache,
+             token](bool blocking) -> nn::GeneralResult<nn::SharedPreparedModel> {
+        return device->prepareModelFromCacheInternal(blocking, deadline, modelCache, dataCache,
+                                                     token);
+    };
+    return ResilientPreparedModel::create(std::move(makePreparedModel));
+}
+
+nn::GeneralResult<nn::SharedBuffer> ResilientDevice::allocate(
+        const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+        const std::vector<nn::BufferRole>& inputRoles,
+        const std::vector<nn::BufferRole>& outputRoles) const {
+    auto self = shared_from_this();
+    ResilientBuffer::Factory makeBuffer =
+            [device = std::move(self), desc, preparedModels, inputRoles,
+             outputRoles](bool blocking) -> nn::GeneralResult<nn::SharedBuffer> {
+        return device->allocateInternal(blocking, desc, preparedModels, inputRoles, outputRoles);
+    };
+    return ResilientBuffer::create(std::move(makeBuffer));
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelInternal(
+        bool blocking, const nn::Model& model, nn::ExecutionPreference preference,
+        nn::Priority priority, nn::OptionalTimePoint deadline,
+        const std::vector<nn::NativeHandle>& modelCache,
+        const std::vector<nn::NativeHandle>& dataCache, const nn::CacheToken& token) const {
+    const auto fn = [&model, preference, priority, deadline, &modelCache, &dataCache,
+                     token](const nn::IDevice& device) {
+        return device.prepareModel(model, preference, priority, deadline, modelCache, dataCache,
+                                   token);
+    };
+    return protect(*this, fn, blocking);
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelFromCacheInternal(
+        bool blocking, nn::OptionalTimePoint deadline,
+        const std::vector<nn::NativeHandle>& modelCache,
+        const std::vector<nn::NativeHandle>& dataCache, const nn::CacheToken& token) const {
+    const auto fn = [deadline, &modelCache, &dataCache, token](const nn::IDevice& device) {
+        return device.prepareModelFromCache(deadline, modelCache, dataCache, token);
+    };
+    return protect(*this, fn, blocking);
+}
+
+nn::GeneralResult<nn::SharedBuffer> ResilientDevice::allocateInternal(
+        bool blocking, const nn::BufferDesc& desc,
+        const std::vector<nn::SharedPreparedModel>& preparedModels,
+        const std::vector<nn::BufferRole>& inputRoles,
+        const std::vector<nn::BufferRole>& outputRoles) const {
+    const auto fn = [&desc, &preparedModels, &inputRoles, &outputRoles](const nn::IDevice& device) {
+        return device.allocate(desc, preparedModels, inputRoles, outputRoles);
+    };
+    return protect(*this, fn, blocking);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
new file mode 100644
index 0000000..1c9ecba
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResilientPreparedModel.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const ResilientPreparedModel>> ResilientPreparedModel::create(
+        Factory makePreparedModel) {
+    if (makePreparedModel == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientPreparedModel::create must have non-empty makePreparedModel";
+    }
+    auto preparedModel = NN_TRY(makePreparedModel(/*blocking=*/true));
+    CHECK(preparedModel != nullptr);
+    return std::make_shared<ResilientPreparedModel>(
+            PrivateConstructorTag{}, std::move(makePreparedModel), std::move(preparedModel));
+}
+
+ResilientPreparedModel::ResilientPreparedModel(PrivateConstructorTag /*tag*/,
+                                               Factory makePreparedModel,
+                                               nn::SharedPreparedModel preparedModel)
+    : kMakePreparedModel(std::move(makePreparedModel)), mPreparedModel(std::move(preparedModel)) {
+    CHECK(kMakePreparedModel != nullptr);
+    CHECK(mPreparedModel != nullptr);
+}
+
+nn::SharedPreparedModel ResilientPreparedModel::getPreparedModel() const {
+    std::lock_guard guard(mMutex);
+    return mPreparedModel;
+}
+
+nn::SharedPreparedModel ResilientPreparedModel::recover(
+        const nn::IPreparedModel* /*failingPreparedModel*/, bool /*blocking*/) const {
+    std::lock_guard guard(mMutex);
+    return mPreparedModel;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ResilientPreparedModel::execute(const nn::Request& request, nn::MeasureTiming measure,
+                                const nn::OptionalTimePoint& deadline,
+                                const nn::OptionalTimeoutDuration& loopTimeoutDuration) const {
+    return getPreparedModel()->execute(request, measure, deadline, loopTimeoutDuration);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+ResilientPreparedModel::executeFenced(
+        const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+        nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+        const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+        const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const {
+    return getPreparedModel()->executeFenced(request, waitFor, measure, deadline,
+                                             loopTimeoutDuration, timeoutDurationAfterFence);
+}
+
+std::any ResilientPreparedModel::getUnderlyingResource() const {
+    return getPreparedModel()->getUnderlyingResource();
+}
+
+}  // namespace android::hardware::neuralnetworks::utils