Merge "Reformat neuralnetworks/1.2/types.hal according to clang-format"
diff --git a/audio/7.0/config/Android.bp b/audio/7.0/config/Android.bp
index 8e87ae0..096ab6a 100644
--- a/audio/7.0/config/Android.bp
+++ b/audio/7.0/config/Android.bp
@@ -13,3 +13,19 @@
     package_name: "android.audio.policy.configuration.V7_0",
     nullability: true,
 }
+
+xsd_config {
+    name: "audio_policy_configuration_V7_0_enums",
+    srcs: ["audio_policy_configuration.xsd"],
+    package_name: "android.audio.policy.configuration.V7_0",
+    nullability: true,
+    enums_only: true,
+}
+
+xsd_config {
+    name: "audio_policy_configuration_V7_0_parser",
+    srcs: ["audio_policy_configuration.xsd"],
+    package_name: "android.audio.policy.configuration.V7_0",
+    nullability: true,
+    parser_only: true,
+}
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
index 531572b..ccaaf98 100644
--- a/audio/7.0/config/audio_policy_configuration.xsd
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -310,13 +310,17 @@
         </xs:restriction>
     </xs:simpleType>
     <xs:simpleType name="vendorExtension">
-        <!-- Vendor extension names must be prefixed by "VX_" to distinguish them from AOSP values.
-             Vendor are encouraged to namespace their module names to avoid conflicts.
-             Example for an hypothetical Google virtual reality device:
-                <devicePort tagName="VR" type="VX_GOOGLE_VR" role="sink">
+        <!-- Vendor extension names must be prefixed by "VX_" to distinguish them from
+             AOSP values. Vendors must namespace their names to avoid conflicts. The
+             namespace part must only use capital latin characters and decimal digits and
+             consist of at least 3 characters. The part of the extension name after the
+             namespace may in addition include underscores. Example for a hypothetical
+             Google virtual reality device:
+
+                 <devicePort tagName="VR" type="VX_GOOGLE_VR" role="sink" />
         -->
         <xs:restriction base="xs:string">
-            <xs:pattern value="VX_[_a-zA-Z0-9]+"/>
+            <xs:pattern value="VX_[A-Z0-9]{3,}_[_A-Z0-9]+"/>
         </xs:restriction>
     </xs:simpleType>
     <xs:simpleType name="extendableAudioDevice">
diff --git a/audio/common/7.0/Android.bp b/audio/common/7.0/Android.bp
index f40f9d1..2f7665e 100644
--- a/audio/common/7.0/Android.bp
+++ b/audio/common/7.0/Android.bp
@@ -25,15 +25,14 @@
 cc_library {
     name: "android.hardware.audio.common@7.0-enums",
     vendor_available: true,
-    generated_headers: ["audio_policy_configuration_V7_0"],
-    generated_sources: ["audio_policy_configuration_V7_0"],
+    generated_headers: ["audio_policy_configuration_V7_0_enums"],
+    generated_sources: ["audio_policy_configuration_V7_0_enums"],
     header_libs: ["libxsdc-utils"],
-    export_generated_headers: ["audio_policy_configuration_V7_0"],
+    export_generated_headers: ["audio_policy_configuration_V7_0_enums"],
     export_header_lib_headers: ["libxsdc-utils"],
     export_include_dirs: ["enums/include"],
     shared_libs: [
         "libbase",
         "liblog",
-        "libxml2",
     ],
 }
diff --git a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
index b427f3a..fe8eee1 100644
--- a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
+++ b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
-#define ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
+#ifndef ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
+#define ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
 
 #include <sys/types.h>
-#include <algorithm>
-#include <cctype>
+#include <regex>
+#include <string>
 
-#include <android_audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0_enums.h>
 
 namespace android::audio::policy::configuration::V7_0 {
 
@@ -219,11 +219,9 @@
 }
 
 static inline bool isVendorExtension(const std::string& s) {
-    // Must match the "vendorExtension" rule from the XSD file.
-    static const std::string vendorPrefix = "VX_";
-    return maybeVendorExtension(s) &&
-           std::all_of(s.begin() + vendorPrefix.size(), s.end(),
-                       [](unsigned char c) { return c == '_' || std::isalnum(c); });
+    // Must be the same as the "vendorExtension" rule from the XSD file.
+    static const std::regex vendorExtension("VX_[A-Z0-9]{3,}_[_A-Z0-9]+");
+    return std::regex_match(s.begin(), s.end(), vendorExtension);
 }
 
 static inline bool isUnknownAudioChannelMask(const std::string& mask) {
@@ -264,4 +262,4 @@
 
 }  // namespace android::audio::policy::configuration::V7_0
 
-#endif  // ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
+#endif  // ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
diff --git a/audio/common/7.0/example/Android.bp b/audio/common/7.0/example/Android.bp
index 93810fb..a85e4fa 100644
--- a/audio/common/7.0/example/Android.bp
+++ b/audio/common/7.0/example/Android.bp
@@ -44,7 +44,6 @@
         "libcutils",
         "libhidlbase",
         "liblog",
-        "libxml2",
         "libutils",
         "android.hardware.audio@7.0",
         "android.hardware.audio.common@7.0",
diff --git a/audio/common/7.0/example/Effect.cpp b/audio/common/7.0/example/Effect.cpp
index 5788811..0621669 100644
--- a/audio/common/7.0/example/Effect.cpp
+++ b/audio/common/7.0/example/Effect.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "EffectsFactory7.0"
 #include <log/log.h>
 
-#include <android_audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
 
 #include "Effect.h"
 
diff --git a/audio/common/all-versions/default/Android.bp b/audio/common/all-versions/default/Android.bp
index 47d4f97..8f55744 100644
--- a/audio/common/all-versions/default/Android.bp
+++ b/audio/common/all-versions/default/Android.bp
@@ -149,7 +149,6 @@
         "android.hardware.audio.common@7.0",
         "android.hardware.audio.common@7.0-enums",
         "libbase",
-        "libxml2",
     ],
     cflags: [
         "-DMAJOR_VERSION=7",
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
index ec6bdf3..e154453 100644
--- a/audio/common/all-versions/default/tests/hidlutils_tests.cpp
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -432,10 +432,16 @@
 // The enums module is too small to have unit tests on its own.
 TEST(HidlUtils, VendorExtension) {
     EXPECT_TRUE(xsd::isVendorExtension("VX_GOOGLE_VR_42"));
+    EXPECT_TRUE(xsd::isVendorExtension("VX_QCM_SPK"));
     EXPECT_FALSE(xsd::isVendorExtension(""));
     EXPECT_FALSE(xsd::isVendorExtension("random string"));
     EXPECT_FALSE(xsd::isVendorExtension("VX_"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_X"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_X_"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_X_X"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_XX_X"));
     EXPECT_FALSE(xsd::isVendorExtension("VX_GOOGLE_$$"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_$CM_SPK"));
 }
 
 TEST(HidlUtils, ConvertInvalidDeviceAddress) {
diff --git a/audio/core/all-versions/default/Android.bp b/audio/core/all-versions/default/Android.bp
index 0aa77d3..f61964e 100644
--- a/audio/core/all-versions/default/Android.bp
+++ b/audio/core/all-versions/default/Android.bp
@@ -146,7 +146,6 @@
         "android.hardware.audio.common@7.0-enums",
         "android.hardware.audio.common@7.0-util",
         "libbase",
-        "libxml2",
     ],
     cflags: [
         "-DMAJOR_VERSION=7",
diff --git a/audio/core/all-versions/default/util/Android.bp b/audio/core/all-versions/default/util/Android.bp
index d0de767..7caf18d 100644
--- a/audio/core/all-versions/default/util/Android.bp
+++ b/audio/core/all-versions/default/util/Android.bp
@@ -104,7 +104,6 @@
         "android.hardware.audio.common@7.0-util",
         "android.hardware.audio@7.0",
         "libbase",
-        "libxml2",
     ],
     cflags: [
         "-DMAJOR_VERSION=7",
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index 109427b..4520dc3 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -153,6 +153,8 @@
     srcs: [
         "7.0/AudioPrimaryHidlHalTest.cpp",
     ],
+    generated_headers: ["audio_policy_configuration_V7_0_parser"],
+    generated_sources: ["audio_policy_configuration_V7_0_parser"],
     static_libs: [
         "android.hardware.audio@7.0",
         "android.hardware.audio.common@7.0",
diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
index 15a2fd9..23e7786 100644
--- a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
@@ -31,7 +31,6 @@
 #include <android/hidl/memory/1.0/IMemory.h>
 #if MAJOR_VERSION >= 7
 #include <android_audio_policy_configuration_V7_0-enums.h>
-#include <android_audio_policy_configuration_V7_0.h>
 #endif
 
 #include <common/all-versions/VersionUtils.h>
diff --git a/graphics/composer/2.4/vts/functional/AndroidTest.xml b/graphics/composer/2.4/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..583aa68
--- /dev/null
+++ b/graphics/composer/2.4/vts/functional/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<configuration description="Runs VtsHalGraphicsComposerV2_4TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.StopServicesSetup">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalGraphicsComposerV2_4TargetTest->/data/local/tmp/VtsHalGraphicsComposerV2_4TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalGraphicsComposerV2_4TargetTest" />
+        <option name="native-test-timeout" value="300000"/>
+    </test>
+</configuration>
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
index 7a099cf..700b050 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -162,7 +162,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(model.operands.size(), operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -360,7 +360,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index 07bf7bc..d07f7d0 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -111,7 +111,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(model.operands.size(), operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -241,7 +241,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 7ae483e..86a417a 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -227,7 +227,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(model.operands.size(), operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -529,7 +529,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 6e74a62..320c74c 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -217,7 +217,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(subgraph.operands.size(), operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(subgraph.operands.size(), operations));
     CHECK(subgraph.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < subgraph.operands.size(); ++i) {
         if (subgraph.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -559,7 +559,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            hal::utils::countNumberOfConsumers(operands.size(), subgraph.operations);
+            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), subgraph.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
index b84d981..6d84e1e 100644
--- a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
@@ -1310,8 +1310,10 @@
 ////////////////////////// ENTRY POINT //////////////////////////////
 
 void validateModel(const std::shared_ptr<IDevice>& device, const Model& model) {
-    const auto numberOfConsumers = nn::countNumberOfConsumers(
-            model.main.operands.size(), nn::convert(model.main.operations).value());
+    const auto numberOfConsumers =
+            nn::countNumberOfConsumers(model.main.operands.size(),
+                                       nn::convert(model.main.operations).value())
+                    .value();
     mutateExecutionOrderTest(device, model, numberOfConsumers);
     mutateOperandTypeTest(device, model);
     mutateOperandRankTest(device, model);
diff --git a/neuralnetworks/utils/adapter/Android.bp b/neuralnetworks/utils/adapter/Android.bp
new file mode 100644
index 0000000..e8dc3e7
--- /dev/null
+++ b/neuralnetworks/utils/adapter/Android.bp
@@ -0,0 +1,37 @@
+//
+// 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.
+//
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_adapter",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: ["src/*"],
+    local_include_dirs: ["include/nnapi/hal"],
+    export_include_dirs: ["include"],
+    static_libs: [
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+        "neuralnetworks_utils_hal_1_2",
+        "neuralnetworks_utils_hal_1_3",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "android.hardware.neuralnetworks@1.2",
+        "android.hardware.neuralnetworks@1.3",
+        "libfmq",
+    ],
+}
diff --git a/neuralnetworks/utils/adapter/include/nnapi/hal/Adapter.h b/neuralnetworks/utils/adapter/include/nnapi/hal/Adapter.h
new file mode 100644
index 0000000..da00a09
--- /dev/null
+++ b/neuralnetworks/utils/adapter/include/nnapi/hal/Adapter.h
@@ -0,0 +1,72 @@
+/*
+ * 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_ADAPTER_ADAPTER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_ADAPTER_H
+
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/Types.h>
+#include <sys/types.h>
+#include <functional>
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::adapter {
+
+/**
+ * A self-contained unit of work to be executed.
+ */
+using Task = std::function<void()>;
+
+/**
+ * A type-erased executor which executes a task asynchronously.
+ *
+ * This executor is also provided with an Application ID (Android User ID) and an optional deadline
+ * for when the caller expects is the upper bound for the amount of time to complete the task.
+ */
+using Executor = std::function<void(Task, uid_t, nn::OptionalTimePoint)>;
+
+/**
+ * Adapt an NNAPI canonical interface object to a HIDL NN HAL interface object.
+ *
+ * The IPreparedModel object created from IDevice::prepareModel or IDevice::preparedModelFromCache
+ * must return "const nn::Model*" from IPreparedModel::getUnderlyingResource().
+ *
+ * @param device NNAPI canonical IDevice interface object to be adapted.
+ * @param executor Type-erased executor to handle executing tasks asynchronously.
+ * @return HIDL NN HAL IDevice interface object.
+ */
+sp<V1_3::IDevice> adapt(nn::SharedDevice device, Executor executor);
+
+/**
+ * Adapt an NNAPI canonical interface object to a HIDL NN HAL interface object.
+ *
+ * The IPreparedModel object created from IDevice::prepareModel or IDevice::preparedModelFromCache
+ * must return "const nn::Model*" from IPreparedModel::getUnderlyingResource().
+ *
+ * This function uses a default executor, which will execute tasks from a detached thread.
+ *
+ * @param device NNAPI canonical IDevice interface object to be adapted.
+ * @return HIDL NN HAL IDevice interface object.
+ */
+sp<V1_3::IDevice> adapt(nn::SharedDevice device);
+
+}  // namespace android::hardware::neuralnetworks::adapter
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_ADAPTER_H
diff --git a/neuralnetworks/utils/adapter/include/nnapi/hal/Buffer.h b/neuralnetworks/utils/adapter/include/nnapi/hal/Buffer.h
new file mode 100644
index 0000000..e53c7d4
--- /dev/null
+++ b/neuralnetworks/utils/adapter/include/nnapi/hal/Buffer.h
@@ -0,0 +1,46 @@
+/*
+ * 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_ADAPTER_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_BUFFER_H
+
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Types.h>
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::adapter {
+
+// Class that adapts nn::IBuffer to V1_3::IBuffer.
+class Buffer final : public V1_3::IBuffer {
+  public:
+    explicit Buffer(nn::SharedBuffer buffer);
+
+    Return<V1_3::ErrorStatus> copyTo(const hidl_memory& dst) override;
+    Return<V1_3::ErrorStatus> copyFrom(const hidl_memory& src,
+                                       const hidl_vec<uint32_t>& dimensions) override;
+
+  private:
+    const nn::SharedBuffer kBuffer;
+};
+
+}  // namespace android::hardware::neuralnetworks::adapter
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_BUFFER_H
diff --git a/neuralnetworks/utils/adapter/include/nnapi/hal/Device.h b/neuralnetworks/utils/adapter/include/nnapi/hal/Device.h
new file mode 100644
index 0000000..148d0a0
--- /dev/null
+++ b/neuralnetworks/utils/adapter/include/nnapi/hal/Device.h
@@ -0,0 +1,96 @@
+/*
+ * 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_ADAPTER_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_DEVICE_H
+
+#include "nnapi/hal/Adapter.h"
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/Types.h>
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::adapter {
+
+using CacheToken = hidl_array<uint8_t, nn::kByteSizeOfCacheToken>;
+
+// Class that adapts nn::IDevice to V1_3::IDevice.
+class Device final : public V1_3::IDevice {
+  public:
+    Device(nn::SharedDevice device, Executor executor);
+
+    Return<void> getCapabilities(getCapabilities_cb cb) override;
+    Return<void> getCapabilities_1_1(getCapabilities_1_1_cb cb) override;
+    Return<void> getCapabilities_1_2(getCapabilities_1_2_cb cb) override;
+    Return<void> getCapabilities_1_3(getCapabilities_1_3_cb cb) override;
+    Return<void> getVersionString(getVersionString_cb cb) override;
+    Return<void> getType(getType_cb cb) override;
+    Return<void> getSupportedExtensions(getSupportedExtensions_cb) override;
+    Return<void> getSupportedOperations(const V1_0::Model& model,
+                                        getSupportedOperations_cb cb) override;
+    Return<void> getSupportedOperations_1_1(const V1_1::Model& model,
+                                            getSupportedOperations_1_1_cb cb) override;
+    Return<void> getSupportedOperations_1_2(const V1_2::Model& model,
+                                            getSupportedOperations_1_2_cb cb) override;
+    Return<void> getSupportedOperations_1_3(const V1_3::Model& model,
+                                            getSupportedOperations_1_3_cb cb) override;
+    Return<void> getNumberOfCacheFilesNeeded(getNumberOfCacheFilesNeeded_cb cb) override;
+    Return<V1_0::ErrorStatus> prepareModel(
+            const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback) override;
+    Return<V1_0::ErrorStatus> prepareModel_1_1(
+            const V1_1::Model& model, V1_1::ExecutionPreference preference,
+            const sp<V1_0::IPreparedModelCallback>& callback) override;
+    Return<V1_0::ErrorStatus> prepareModel_1_2(
+            const V1_2::Model& model, V1_1::ExecutionPreference preference,
+            const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+            const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback) override;
+    Return<V1_3::ErrorStatus> prepareModel_1_3(
+            const V1_3::Model& model, V1_1::ExecutionPreference preference, V1_3::Priority priority,
+            const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
+            const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
+            const sp<V1_3::IPreparedModelCallback>& callback) override;
+    Return<V1_0::ErrorStatus> prepareModelFromCache(
+            const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+            const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback) override;
+    Return<V1_3::ErrorStatus> prepareModelFromCache_1_3(
+            const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
+            const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
+            const sp<V1_3::IPreparedModelCallback>& callback) override;
+    Return<V1_0::DeviceStatus> getStatus() override;
+    Return<void> allocate(const V1_3::BufferDesc& desc,
+                          const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels,
+                          const hidl_vec<V1_3::BufferRole>& inputRoles,
+                          const hidl_vec<V1_3::BufferRole>& outputRoles, allocate_cb cb) override;
+
+  private:
+    const nn::SharedDevice kDevice;
+    const Executor kExecutor;
+};
+
+}  // namespace android::hardware::neuralnetworks::adapter
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_DEVICE_H
diff --git a/neuralnetworks/utils/adapter/include/nnapi/hal/PreparedModel.h b/neuralnetworks/utils/adapter/include/nnapi/hal/PreparedModel.h
new file mode 100644
index 0000000..65763b8
--- /dev/null
+++ b/neuralnetworks/utils/adapter/include/nnapi/hal/PreparedModel.h
@@ -0,0 +1,79 @@
+/*
+ * 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_ADAPTER_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_PREPARED_MODEL_H
+
+#include "nnapi/hal/Adapter.h"
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Types.h>
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::adapter {
+
+// Class that adapts nn::IPreparedModel to V1_3::IPreparedModel.
+class PreparedModel final : public V1_3::IPreparedModel {
+  public:
+    PreparedModel(nn::SharedPreparedModel preparedModel, Executor executor, uid_t userId);
+
+    Return<V1_0::ErrorStatus> execute(const V1_0::Request& request,
+                                      const sp<V1_0::IExecutionCallback>& callback) override;
+    Return<V1_0::ErrorStatus> execute_1_2(const V1_0::Request& request, V1_2::MeasureTiming measure,
+                                          const sp<V1_2::IExecutionCallback>& callback) override;
+    Return<V1_3::ErrorStatus> execute_1_3(const V1_3::Request& request, V1_2::MeasureTiming measure,
+                                          const V1_3::OptionalTimePoint& deadline,
+                                          const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+                                          const sp<V1_3::IExecutionCallback>& callback) override;
+    Return<void> executeSynchronously(const V1_0::Request& request, V1_2::MeasureTiming measure,
+                                      executeSynchronously_cb cb) override;
+    Return<void> executeSynchronously_1_3(const V1_3::Request& request, V1_2::MeasureTiming measure,
+                                          const V1_3::OptionalTimePoint& deadline,
+                                          const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+                                          executeSynchronously_1_3_cb cb) override;
+    Return<void> configureExecutionBurst(
+            const sp<V1_2::IBurstCallback>& callback,
+            const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+            const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
+            configureExecutionBurst_cb cb) override;
+    Return<void> executeFenced(const V1_3::Request& request, const hidl_vec<hidl_handle>& waitFor,
+                               V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline,
+                               const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+                               const V1_3::OptionalTimeoutDuration& duration,
+                               executeFenced_cb callback) override;
+
+    nn::SharedPreparedModel getUnderlyingPreparedModel() const;
+
+  private:
+    const nn::SharedPreparedModel kPreparedModel;
+    const Executor kExecutor;
+    const uid_t kUserId;
+};
+
+}  // namespace android::hardware::neuralnetworks::adapter
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_PREPARED_MODEL_H
diff --git a/neuralnetworks/utils/adapter/src/Adapter.cpp b/neuralnetworks/utils/adapter/src/Adapter.cpp
new file mode 100644
index 0000000..d6f53f0
--- /dev/null
+++ b/neuralnetworks/utils/adapter/src/Adapter.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "Adapter.h"
+
+#include "Device.h"
+
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/Types.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <memory>
+#include <thread>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::adapter {
+
+sp<V1_3::IDevice> adapt(nn::SharedDevice device, Executor executor) {
+    return sp<Device>::make(std::move(device), std::move(executor));
+}
+
+sp<V1_3::IDevice> adapt(nn::SharedDevice device) {
+    Executor defaultExecutor = [](Task task, uid_t /*uid*/, nn::OptionalTimePoint /*deadline*/) {
+        std::thread(std::move(task)).detach();
+    };
+    return adapt(std::move(device), std::move(defaultExecutor));
+}
+
+}  // namespace android::hardware::neuralnetworks::adapter
diff --git a/neuralnetworks/utils/adapter/src/Buffer.cpp b/neuralnetworks/utils/adapter/src/Buffer.cpp
new file mode 100644
index 0000000..3a04bf6
--- /dev/null
+++ b/neuralnetworks/utils/adapter/src/Buffer.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "Buffer.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.3/Utils.h>
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::adapter {
+namespace {
+
+template <typename Type>
+auto convertInput(const Type& object) -> decltype(nn::convert(std::declval<Type>())) {
+    auto result = nn::convert(object);
+    if (!result.has_value()) {
+        result.error().code = nn::ErrorStatus::INVALID_ARGUMENT;
+    }
+    return result;
+}
+
+nn::GeneralResult<void> copyTo(const nn::SharedBuffer& buffer, const hidl_memory& dst) {
+    const auto memory = NN_TRY(convertInput(dst));
+    NN_TRY(buffer->copyTo(memory));
+    return {};
+}
+
+nn::GeneralResult<void> copyFrom(const nn::SharedBuffer& buffer, const hidl_memory& src,
+                                 const hidl_vec<uint32_t>& dimensions) {
+    const auto memory = NN_TRY(convertInput(src));
+    NN_TRY(buffer->copyFrom(memory, dimensions));
+    return {};
+}
+
+}  // namespace
+
+Buffer::Buffer(nn::SharedBuffer buffer) : kBuffer(std::move(buffer)) {
+    CHECK(kBuffer != nullptr);
+}
+
+Return<V1_3::ErrorStatus> Buffer::copyTo(const hidl_memory& dst) {
+    auto result = adapter::copyTo(kBuffer, dst);
+    if (!result.has_value()) {
+        const auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Buffer::copyTo failed with " << code << ": " << message;
+        return V1_3::utils::convert(code).value();
+    }
+    return V1_3::ErrorStatus::NONE;
+}
+
+Return<V1_3::ErrorStatus> Buffer::copyFrom(const hidl_memory& src,
+                                           const hidl_vec<uint32_t>& dimensions) {
+    auto result = adapter::copyFrom(kBuffer, src, dimensions);
+    if (!result.has_value()) {
+        const auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Buffer::copyFrom failed with " << code << ": " << message;
+        return V1_3::utils::convert(code).value();
+    }
+    return V1_3::ErrorStatus::NONE;
+}
+
+}  // namespace android::hardware::neuralnetworks::adapter
diff --git a/neuralnetworks/utils/adapter/src/Device.cpp b/neuralnetworks/utils/adapter/src/Device.cpp
new file mode 100644
index 0000000..96142c3
--- /dev/null
+++ b/neuralnetworks/utils/adapter/src/Device.cpp
@@ -0,0 +1,556 @@
+/*
+ * 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 "Device.h"
+
+#include "Buffer.h"
+#include "PreparedModel.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <hwbinder/IPCThreadState.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 <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/Utils.h>
+#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/1.1/Utils.h>
+#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/Utils.h>
+#include <nnapi/hal/1.3/Conversions.h>
+#include <nnapi/hal/1.3/Utils.h>
+#include <sys/types.h>
+
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::adapter {
+namespace {
+
+template <typename Type>
+auto convertInput(const Type& object) -> decltype(nn::convert(std::declval<Type>())) {
+    auto result = nn::convert(object);
+    if (!result.has_value()) {
+        result.error().code = nn::ErrorStatus::INVALID_ARGUMENT;
+    }
+    return result;
+}
+
+using PrepareModelResult = nn::GeneralResult<nn::SharedPreparedModel>;
+
+sp<PreparedModel> adaptPreparedModel(nn::SharedPreparedModel preparedModel, Executor executor,
+                                     uid_t userId) {
+    if (preparedModel == nullptr) {
+        return nullptr;
+    }
+    return sp<PreparedModel>::make(std::move(preparedModel), std::move(executor), userId);
+}
+
+void notify(V1_0::IPreparedModelCallback* callback, nn::ErrorStatus status,
+            const sp<PreparedModel>& hidlPreparedModel) {
+    if (callback != nullptr) {
+        const auto hidlStatus = V1_0::utils::convert(status).value();
+        const auto ret = callback->notify(hidlStatus, hidlPreparedModel);
+        if (!ret.isOk()) {
+            LOG(ERROR) << "V1_0::IPreparedModelCallback::notify failed with " << ret.description();
+        }
+    }
+}
+
+void notify(V1_2::IPreparedModelCallback* callback, nn::ErrorStatus status,
+            const sp<PreparedModel>& hidlPreparedModel) {
+    if (callback != nullptr) {
+        const auto hidlStatus = V1_2::utils::convert(status).value();
+        const auto ret = callback->notify_1_2(hidlStatus, hidlPreparedModel);
+        if (!ret.isOk()) {
+            LOG(ERROR) << "V1_2::IPreparedModelCallback::notify_1_2 failed with "
+                       << ret.description();
+        }
+    }
+}
+
+void notify(V1_3::IPreparedModelCallback* callback, nn::ErrorStatus status,
+            const sp<PreparedModel>& hidlPreparedModel) {
+    if (callback != nullptr) {
+        const auto hidlStatus = V1_3::utils::convert(status).value();
+        const auto ret = callback->notify_1_3(hidlStatus, hidlPreparedModel);
+        if (!ret.isOk()) {
+            LOG(ERROR) << "V1_3::IPreparedModelCallback::notify_1_3 failed with "
+                       << ret.description();
+        }
+    }
+}
+
+template <typename CallbackType>
+void notify(CallbackType* callback, PrepareModelResult result, Executor executor, uid_t userId) {
+    if (!result.has_value()) {
+        const auto [message, status] = std::move(result).error();
+        LOG(ERROR) << message;
+        notify(callback, status, nullptr);
+    } else {
+        auto preparedModel = std::move(result).value();
+        auto hidlPreparedModel =
+                adaptPreparedModel(std::move(preparedModel), std::move(executor), userId);
+        notify(callback, nn::ErrorStatus::NONE, std::move(hidlPreparedModel));
+    }
+}
+
+template <typename ModelType>
+nn::GeneralResult<hidl_vec<bool>> getSupportedOperations(const nn::SharedDevice& device,
+                                                         const ModelType& model) {
+    const auto nnModel = NN_TRY(convertInput(model));
+    return NN_TRY(device->getSupportedOperations(nnModel));
+}
+
+nn::GeneralResult<void> prepareModel(const nn::SharedDevice& device, const Executor& executor,
+                                     const V1_0::Model& model,
+                                     const sp<V1_0::IPreparedModelCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    auto nnModel = NN_TRY(convertInput(model));
+
+    const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
+    Task task = [device, nnModel = std::move(nnModel), userId, executor, callback] {
+        auto result = device->prepareModel(nnModel, nn::ExecutionPreference::DEFAULT,
+                                           nn::Priority::DEFAULT, {}, {}, {}, {});
+        notify(callback.get(), std::move(result), executor, userId);
+    };
+    executor(std::move(task), userId, {});
+
+    return {};
+}
+
+nn::GeneralResult<void> prepareModel_1_1(const nn::SharedDevice& device, const Executor& executor,
+                                         const V1_1::Model& model,
+                                         V1_1::ExecutionPreference preference,
+                                         const sp<V1_0::IPreparedModelCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    auto nnModel = NN_TRY(convertInput(model));
+    const auto nnPreference = NN_TRY(convertInput(preference));
+
+    const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
+    Task task = [device, nnModel = std::move(nnModel), nnPreference, userId, executor, callback] {
+        auto result =
+                device->prepareModel(nnModel, nnPreference, nn::Priority::DEFAULT, {}, {}, {}, {});
+        notify(callback.get(), std::move(result), executor, userId);
+    };
+    executor(std::move(task), userId, {});
+
+    return {};
+}
+
+nn::GeneralResult<void> prepareModel_1_2(const nn::SharedDevice& device, const Executor& executor,
+                                         const V1_2::Model& model,
+                                         V1_1::ExecutionPreference preference,
+                                         const hidl_vec<hidl_handle>& modelCache,
+                                         const hidl_vec<hidl_handle>& dataCache,
+                                         const CacheToken& token,
+                                         const sp<V1_2::IPreparedModelCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    auto nnModel = NN_TRY(convertInput(model));
+    const auto nnPreference = NN_TRY(convertInput(preference));
+    auto nnModelCache = NN_TRY(convertInput(modelCache));
+    auto nnDataCache = NN_TRY(convertInput(dataCache));
+    const auto nnToken = nn::CacheToken(token);
+
+    const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
+    Task task = [device, nnModel = std::move(nnModel), nnPreference,
+                 nnModelCache = std::move(nnModelCache), nnDataCache = std::move(nnDataCache),
+                 nnToken, userId, executor, callback] {
+        auto result = device->prepareModel(nnModel, nnPreference, nn::Priority::DEFAULT, {},
+                                           nnModelCache, nnDataCache, nnToken);
+        notify(callback.get(), std::move(result), executor, userId);
+    };
+    executor(std::move(task), userId, {});
+
+    return {};
+}
+
+nn::GeneralResult<void> prepareModel_1_3(
+        const nn::SharedDevice& device, const Executor& executor, const V1_3::Model& model,
+        V1_1::ExecutionPreference preference, V1_3::Priority priority,
+        const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
+        const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
+        const sp<V1_3::IPreparedModelCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    auto nnModel = NN_TRY(convertInput(model));
+    const auto nnPreference = NN_TRY(convertInput(preference));
+    const auto nnPriority = NN_TRY(convertInput(priority));
+    const auto nnDeadline = NN_TRY(convertInput(deadline));
+    auto nnModelCache = NN_TRY(convertInput(modelCache));
+    auto nnDataCache = NN_TRY(convertInput(dataCache));
+    const auto nnToken = nn::CacheToken(token);
+
+    const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
+    Task task = [device, nnModel = std::move(nnModel), nnPreference, nnPriority, nnDeadline,
+                 nnModelCache = std::move(nnModelCache), nnDataCache = std::move(nnDataCache),
+                 nnToken, userId, executor, callback] {
+        auto result = device->prepareModel(nnModel, nnPreference, nnPriority, nnDeadline,
+                                           nnModelCache, nnDataCache, nnToken);
+        notify(callback.get(), std::move(result), executor, userId);
+    };
+    executor(std::move(task), userId, nnDeadline);
+
+    return {};
+}
+
+nn::GeneralResult<void> prepareModelFromCache(const nn::SharedDevice& device,
+                                              const Executor& executor,
+                                              const hidl_vec<hidl_handle>& modelCache,
+                                              const hidl_vec<hidl_handle>& dataCache,
+                                              const CacheToken& token,
+                                              const sp<V1_2::IPreparedModelCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    auto nnModelCache = NN_TRY(convertInput(modelCache));
+    auto nnDataCache = NN_TRY(convertInput(dataCache));
+    const auto nnToken = nn::CacheToken(token);
+
+    const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
+    Task task = [device, nnModelCache = std::move(nnModelCache),
+                 nnDataCache = std::move(nnDataCache), nnToken, userId, executor, callback] {
+        auto result = device->prepareModelFromCache({}, nnModelCache, nnDataCache, nnToken);
+        notify(callback.get(), std::move(result), executor, userId);
+    };
+    executor(std::move(task), userId, {});
+
+    return {};
+}
+
+nn::GeneralResult<void> prepareModelFromCache_1_3(
+        const nn::SharedDevice& device, const Executor& executor,
+        const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
+        const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
+        const sp<V1_3::IPreparedModelCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    const auto nnDeadline = NN_TRY(convertInput(deadline));
+    auto nnModelCache = NN_TRY(convertInput(modelCache));
+    auto nnDataCache = NN_TRY(convertInput(dataCache));
+    const auto nnToken = nn::CacheToken(token);
+
+    const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
+    auto task = [device, nnDeadline, nnModelCache = std::move(nnModelCache),
+                 nnDataCache = std::move(nnDataCache), nnToken, userId, executor, callback] {
+        auto result = device->prepareModelFromCache(nnDeadline, nnModelCache, nnDataCache, nnToken);
+        notify(callback.get(), std::move(result), executor, userId);
+    };
+    executor(std::move(task), userId, nnDeadline);
+
+    return {};
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> downcast(const sp<V1_3::IPreparedModel>& preparedModel) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "preparedModel is nullptr";
+    }
+    if (preparedModel->isRemote()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Cannot convert remote models";
+    }
+
+    // This static_cast is safe because adapter::PreparedModel is the only class that implements
+    // the IPreparedModel interface in the adapter service code.
+    const auto* casted = static_cast<const PreparedModel*>(preparedModel.get());
+    return casted->getUnderlyingPreparedModel();
+}
+
+nn::GeneralResult<std::vector<nn::SharedPreparedModel>> downcastAll(
+        const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels) {
+    std::vector<nn::SharedPreparedModel> canonical;
+    canonical.reserve(preparedModels.size());
+    for (const auto& preparedModel : preparedModels) {
+        canonical.push_back(NN_TRY(downcast(preparedModel)));
+    }
+    return canonical;
+}
+
+nn::GeneralResult<std::pair<sp<V1_3::IBuffer>, uint32_t>> allocate(
+        const nn::SharedDevice& device, const V1_3::BufferDesc& desc,
+        const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels,
+        const hidl_vec<V1_3::BufferRole>& inputRoles,
+        const hidl_vec<V1_3::BufferRole>& outputRoles) {
+    auto nnDesc = NN_TRY(convertInput(desc));
+    auto nnPreparedModels = NN_TRY(downcastAll(preparedModels));
+    auto nnInputRoles = NN_TRY(convertInput(inputRoles));
+    auto nnOutputRoles = NN_TRY(convertInput(outputRoles));
+
+    auto buffer = NN_TRY(device->allocate(nnDesc, nnPreparedModels, nnInputRoles, nnOutputRoles));
+
+    const nn::Request::MemoryDomainToken token = buffer->getToken();
+    auto hidlBuffer = sp<Buffer>::make(std::move(buffer));
+    return std::make_pair(std::move(hidlBuffer), static_cast<uint32_t>(token));
+}
+
+}  // namespace
+
+Device::Device(nn::SharedDevice device, Executor executor)
+    : kDevice(std::move(device)), kExecutor(std::move(executor)) {
+    CHECK(kDevice != nullptr);
+    CHECK(kExecutor != nullptr);
+}
+
+Return<void> Device::getCapabilities(getCapabilities_cb cb) {
+    const auto capabilities = V1_0::utils::convert(kDevice->getCapabilities()).value();
+    cb(V1_0::ErrorStatus::NONE, capabilities);
+    return Void();
+}
+
+Return<void> Device::getCapabilities_1_1(getCapabilities_1_1_cb cb) {
+    const auto capabilities = V1_1::utils::convert(kDevice->getCapabilities()).value();
+    cb(V1_0::ErrorStatus::NONE, capabilities);
+    return Void();
+}
+
+Return<void> Device::getCapabilities_1_2(getCapabilities_1_2_cb cb) {
+    const auto capabilities = V1_2::utils::convert(kDevice->getCapabilities()).value();
+    cb(V1_0::ErrorStatus::NONE, capabilities);
+    return Void();
+}
+
+Return<void> Device::getCapabilities_1_3(getCapabilities_1_3_cb cb) {
+    const auto capabilities = V1_3::utils::convert(kDevice->getCapabilities()).value();
+    cb(V1_3::ErrorStatus::NONE, capabilities);
+    return Void();
+}
+
+Return<void> Device::getVersionString(getVersionString_cb cb) {
+    cb(V1_0::ErrorStatus::NONE, kDevice->getVersionString());
+    return Void();
+}
+
+Return<void> Device::getType(getType_cb cb) {
+    const auto maybeDeviceType = V1_2::utils::convert(kDevice->getType());
+    if (!maybeDeviceType.has_value()) {
+        const auto& [message, code] = maybeDeviceType.error();
+        LOG(ERROR) << "adapter::Device::getType failed with " << code << ": " << message;
+        cb(V1_2::utils::convert(code).value(), {});
+    } else {
+        cb(V1_0::ErrorStatus::NONE, maybeDeviceType.value());
+    }
+    return Void();
+}
+
+Return<void> Device::getSupportedExtensions(getSupportedExtensions_cb cb) {
+    const auto maybeSupportedExtensions = V1_2::utils::convert(kDevice->getSupportedExtensions());
+    if (!maybeSupportedExtensions.has_value()) {
+        const auto& [message, code] = maybeSupportedExtensions.error();
+        LOG(ERROR) << "adapter::Device::getSupportedExtensions failed with " << code << ": "
+                   << message;
+        cb(V1_2::utils::convert(code).value(), {});
+    } else {
+        cb(V1_0::ErrorStatus::NONE, maybeSupportedExtensions.value());
+    }
+    return Void();
+}
+
+Return<void> Device::getSupportedOperations(const V1_0::Model& model,
+                                            getSupportedOperations_cb cb) {
+    const auto result = adapter::getSupportedOperations(kDevice, model);
+    if (!result.has_value()) {
+        const auto& [message, code] = result.error();
+        LOG(ERROR) << "adapter::Device::getSupportedOperations_1_0 failed with " << code << ": "
+                   << message;
+        cb(V1_0::utils::convert(code).value(), {});
+    } else {
+        cb(V1_0::ErrorStatus::NONE, result.value());
+    }
+    return Void();
+}
+
+Return<void> Device::getSupportedOperations_1_1(const V1_1::Model& model,
+                                                getSupportedOperations_1_1_cb cb) {
+    const auto result = adapter::getSupportedOperations(kDevice, model);
+    if (!result.has_value()) {
+        const auto& [message, code] = result.error();
+        LOG(ERROR) << "adapter::Device::getSupportedOperations_1_1 failed with " << code << ": "
+                   << message;
+        cb(V1_1::utils::convert(code).value(), {});
+    } else {
+        cb(V1_0::ErrorStatus::NONE, result.value());
+    }
+    return Void();
+}
+
+Return<void> Device::getSupportedOperations_1_2(const V1_2::Model& model,
+                                                getSupportedOperations_1_2_cb cb) {
+    const auto result = adapter::getSupportedOperations(kDevice, model);
+    if (!result.has_value()) {
+        const auto& [message, code] = result.error();
+        LOG(ERROR) << "adapter::Device::getSupportedOperations_1_2 failed with " << code << ": "
+                   << message;
+        cb(V1_2::utils::convert(code).value(), {});
+    } else {
+        cb(V1_0::ErrorStatus::NONE, result.value());
+    }
+    return Void();
+}
+
+Return<void> Device::getSupportedOperations_1_3(const V1_3::Model& model,
+                                                getSupportedOperations_1_3_cb cb) {
+    const auto result = adapter::getSupportedOperations(kDevice, model);
+    if (!result.has_value()) {
+        const auto& [message, code] = result.error();
+        LOG(ERROR) << "adapter::Device::getSupportedOperations_1_3 failed with " << code << ": "
+                   << message;
+        cb(V1_3::utils::convert(code).value(), {});
+    } else {
+        cb(V1_3::ErrorStatus::NONE, result.value());
+    }
+    return Void();
+}
+
+Return<void> Device::getNumberOfCacheFilesNeeded(getNumberOfCacheFilesNeeded_cb cb) {
+    const auto [numModelCache, numDataCache] = kDevice->getNumberOfCacheFilesNeeded();
+    cb(V1_0::ErrorStatus::NONE, numModelCache, numDataCache);
+    return Void();
+}
+
+Return<V1_0::ErrorStatus> Device::prepareModel(const V1_0::Model& model,
+                                               const sp<V1_0::IPreparedModelCallback>& callback) {
+    auto result = adapter::prepareModel(kDevice, kExecutor, model, callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Device::prepareModel failed with " << code << ": " << message;
+        notify(callback.get(), code, nullptr);
+        return V1_0::utils::convert(code).value();
+    }
+    return V1_0::ErrorStatus::NONE;
+}
+
+Return<V1_0::ErrorStatus> Device::prepareModel_1_1(
+        const V1_1::Model& model, V1_1::ExecutionPreference preference,
+        const sp<V1_0::IPreparedModelCallback>& callback) {
+    auto result = adapter::prepareModel_1_1(kDevice, kExecutor, model, preference, callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Device::prepareModel_1_1 failed with " << code << ": " << message;
+        notify(callback.get(), code, nullptr);
+        return V1_1::utils::convert(code).value();
+    }
+    return V1_0::ErrorStatus::NONE;
+}
+
+Return<V1_0::ErrorStatus> Device::prepareModel_1_2(
+        const V1_2::Model& model, V1_1::ExecutionPreference preference,
+        const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+        const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback) {
+    auto result = adapter::prepareModel_1_2(kDevice, kExecutor, model, preference, modelCache,
+                                            dataCache, token, callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Device::prepareModel_1_2 failed with " << code << ": " << message;
+        notify(callback.get(), code, nullptr);
+        return V1_2::utils::convert(code).value();
+    }
+    return V1_0::ErrorStatus::NONE;
+}
+
+Return<V1_3::ErrorStatus> Device::prepareModel_1_3(
+        const V1_3::Model& model, V1_1::ExecutionPreference preference, V1_3::Priority priority,
+        const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
+        const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
+        const sp<V1_3::IPreparedModelCallback>& callback) {
+    auto result = adapter::prepareModel_1_3(kDevice, kExecutor, model, preference, priority,
+                                            deadline, modelCache, dataCache, token, callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Device::prepareModel_1_3 failed with " << code << ": " << message;
+        notify(callback.get(), code, nullptr);
+        return V1_3::utils::convert(code).value();
+    }
+    return V1_3::ErrorStatus::NONE;
+}
+
+Return<V1_0::ErrorStatus> Device::prepareModelFromCache(
+        const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
+        const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback) {
+    auto result = adapter::prepareModelFromCache(kDevice, kExecutor, modelCache, dataCache, token,
+                                                 callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Device::prepareModelFromCache failed with " << code << ": "
+                   << message;
+        notify(callback.get(), code, nullptr);
+        return V1_2::utils::convert(code).value();
+    }
+    return V1_0::ErrorStatus::NONE;
+}
+
+Return<V1_3::ErrorStatus> Device::prepareModelFromCache_1_3(
+        const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
+        const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
+        const sp<V1_3::IPreparedModelCallback>& callback) {
+    auto result = adapter::prepareModelFromCache_1_3(kDevice, kExecutor, deadline, modelCache,
+                                                     dataCache, token, callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Device::prepareModelFromCache_1_3 failed with " << code << ": "
+                   << message;
+        notify(callback.get(), code, nullptr);
+        return V1_3::utils::convert(code).value();
+    }
+    return V1_3::ErrorStatus::NONE;
+}
+
+Return<V1_0::DeviceStatus> Device::getStatus() {
+    return V1_0::DeviceStatus::AVAILABLE;
+}
+
+Return<void> Device::allocate(const V1_3::BufferDesc& desc,
+                              const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels,
+                              const hidl_vec<V1_3::BufferRole>& inputRoles,
+                              const hidl_vec<V1_3::BufferRole>& outputRoles, allocate_cb cb) {
+    auto result = adapter::allocate(kDevice, desc, preparedModels, inputRoles, outputRoles);
+    if (!result.has_value()) {
+        const auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::Device::allocate failed with " << code << ": " << message;
+        cb(V1_3::utils::convert(code).value(), nullptr, /*token=*/0);
+        return Void();
+    }
+    auto [buffer, token] = std::move(result).value();
+    cb(V1_3::ErrorStatus::NONE, buffer, token);
+    return Void();
+}
+
+}  // namespace android::hardware::neuralnetworks::adapter
diff --git a/neuralnetworks/utils/adapter/src/PreparedModel.cpp b/neuralnetworks/utils/adapter/src/PreparedModel.cpp
new file mode 100644
index 0000000..8968c2c
--- /dev/null
+++ b/neuralnetworks/utils/adapter/src/PreparedModel.cpp
@@ -0,0 +1,417 @@
+/*
+ * 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 "PreparedModel.h"
+
+#include <ExecutionBurstServer.h>
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <hwbinder/IPCThreadState.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Utils.h>
+#include <nnapi/hal/1.2/Utils.h>
+#include <nnapi/hal/1.3/Conversions.h>
+#include <nnapi/hal/1.3/Utils.h>
+#include <nnapi/hal/HandleError.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <thread>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::adapter {
+namespace {
+
+template <typename Type>
+auto convertInput(const Type& object) -> decltype(nn::convert(std::declval<Type>())) {
+    auto result = nn::convert(object);
+    if (!result.has_value()) {
+        result.error().code = nn::ErrorStatus::INVALID_ARGUMENT;
+    }
+    return result;
+}
+
+class FencedExecutionCallback final : public V1_3::IFencedExecutionCallback {
+  public:
+    explicit FencedExecutionCallback(const nn::ExecuteFencedInfoCallback& callback)
+        : kCallback(callback) {
+        CHECK(callback != nullptr);
+    }
+
+    Return<void> getExecutionInfo(getExecutionInfo_cb cb) override {
+        const auto result = kCallback();
+        if (!result.has_value()) {
+            const auto& [message, code] = result.error();
+            const auto status =
+                    V1_3::utils::convert(code).value_or(V1_3::ErrorStatus::GENERAL_FAILURE);
+            LOG(ERROR) << message;
+            cb(status, V1_2::utils::kNoTiming, V1_2::utils::kNoTiming);
+            return Void();
+        }
+        const auto [timingLaunched, timingFenced] = result.value();
+        const auto hidlTimingLaunched = V1_3::utils::convert(timingLaunched).value();
+        const auto hidlTimingFenced = V1_3::utils::convert(timingFenced).value();
+        cb(V1_3::ErrorStatus::NONE, hidlTimingLaunched, hidlTimingFenced);
+        return Void();
+    }
+
+  private:
+    const nn::ExecuteFencedInfoCallback kCallback;
+};
+
+using ExecutionResult = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
+
+void notify(V1_0::IExecutionCallback* callback, nn::ErrorStatus status,
+            const std::vector<nn::OutputShape>& /*outputShapes*/, const nn::Timing& /*timing*/) {
+    if (callback != nullptr) {
+        const auto hidlStatus = V1_0::utils::convert(status).value();
+        const auto ret = callback->notify(hidlStatus);
+        if (!ret.isOk()) {
+            LOG(ERROR) << "V1_0::IExecutionCallback::notify failed with " << ret.description();
+        }
+    }
+}
+
+void notify(V1_2::IExecutionCallback* callback, nn::ErrorStatus status,
+            const std::vector<nn::OutputShape>& outputShapes, const nn::Timing& timing) {
+    if (callback != nullptr) {
+        const auto hidlStatus = V1_2::utils::convert(status).value();
+        const auto hidlOutputShapes = V1_2::utils::convert(outputShapes).value();
+        const auto hidlTiming = V1_2::utils::convert(timing).value();
+        const auto ret = callback->notify_1_2(hidlStatus, hidlOutputShapes, hidlTiming);
+        if (!ret.isOk()) {
+            LOG(ERROR) << "V1_2::IExecutionCallback::notify_1_2 failed with " << ret.description();
+        }
+    }
+}
+
+void notify(V1_3::IExecutionCallback* callback, nn::ErrorStatus status,
+            const std::vector<nn::OutputShape>& outputShapes, const nn::Timing& timing) {
+    if (callback != nullptr) {
+        const auto hidlStatus = V1_3::utils::convert(status).value();
+        const auto hidlOutputShapes = V1_3::utils::convert(outputShapes).value();
+        const auto hidlTiming = V1_3::utils::convert(timing).value();
+        const auto ret = callback->notify_1_3(hidlStatus, hidlOutputShapes, hidlTiming);
+        if (!ret.isOk()) {
+            LOG(ERROR) << "V1_3::IExecutionCallback::notify_1_3 failed with " << ret.description();
+        }
+    }
+}
+
+template <typename CallbackType>
+void notify(CallbackType* callback, ExecutionResult result) {
+    if (!result.has_value()) {
+        const auto [message, status, outputShapes] = std::move(result).error();
+        LOG(ERROR) << message;
+        notify(callback, status, outputShapes, {});
+    } else {
+        const auto [outputShapes, timing] = std::move(result).value();
+        notify(callback, nn::ErrorStatus::NONE, outputShapes, timing);
+    }
+}
+
+nn::GeneralResult<void> execute(const nn::SharedPreparedModel& preparedModel, uid_t userId,
+                                const Executor& executor, const V1_0::Request& request,
+                                const sp<V1_0::IExecutionCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    auto nnRequest = NN_TRY(convertInput(request));
+
+    const std::any resource = preparedModel->getUnderlyingResource();
+    if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
+        CHECK(*model != nullptr);
+        NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
+                                         nn::ErrorStatus::INVALID_ARGUMENT));
+    }
+
+    Task task = [preparedModel, nnRequest = std::move(nnRequest), callback] {
+        auto result = preparedModel->execute(nnRequest, nn::MeasureTiming::NO, {}, {});
+        notify(callback.get(), std::move(result));
+    };
+    executor(std::move(task), userId, {});
+
+    return {};
+}
+
+nn::GeneralResult<void> execute_1_2(const nn::SharedPreparedModel& preparedModel, uid_t userId,
+                                    const Executor& executor, const V1_0::Request& request,
+                                    V1_2::MeasureTiming measure,
+                                    const sp<V1_2::IExecutionCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    auto nnRequest = NN_TRY(convertInput(request));
+    const auto nnMeasure = NN_TRY(convertInput(measure));
+
+    const std::any resource = preparedModel->getUnderlyingResource();
+    if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
+        CHECK(*model != nullptr);
+        NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
+                                         nn::ErrorStatus::INVALID_ARGUMENT));
+    }
+
+    Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, callback] {
+        auto result = preparedModel->execute(nnRequest, nnMeasure, {}, {});
+        notify(callback.get(), std::move(result));
+    };
+    executor(std::move(task), userId, {});
+
+    return {};
+}
+
+nn::GeneralResult<void> execute_1_3(const nn::SharedPreparedModel& preparedModel, uid_t userId,
+                                    const Executor& executor, const V1_3::Request& request,
+                                    V1_2::MeasureTiming measure,
+                                    const V1_3::OptionalTimePoint& deadline,
+                                    const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+                                    const sp<V1_3::IExecutionCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
+    }
+
+    auto nnRequest = NN_TRY(convertInput(request));
+    const auto nnMeasure = NN_TRY(convertInput(measure));
+    const auto nnDeadline = NN_TRY(convertInput(deadline));
+    const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration));
+
+    const std::any resource = preparedModel->getUnderlyingResource();
+    if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
+        CHECK(*model != nullptr);
+        NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
+                                         nn::ErrorStatus::INVALID_ARGUMENT));
+    }
+
+    Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, nnDeadline,
+                 nnLoopTimeoutDuration, callback] {
+        auto result =
+                preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration);
+        notify(callback.get(), std::move(result));
+    };
+    executor(std::move(task), userId, nnDeadline);
+
+    return {};
+}
+
+nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> executeSynchronously(
+        const nn::SharedPreparedModel& preparedModel, const V1_0::Request& request,
+        V1_2::MeasureTiming measure) {
+    const auto nnRequest = NN_TRY(utils::makeExecutionFailure(convertInput(request)));
+    const auto nnMeasure = NN_TRY(utils::makeExecutionFailure(convertInput(measure)));
+
+    const auto [outputShapes, timing] =
+            NN_TRY(preparedModel->execute(nnRequest, nnMeasure, {}, {}));
+
+    auto hidlOutputShapes = NN_TRY(utils::makeExecutionFailure(V1_2::utils::convert(outputShapes)));
+    const auto hidlTiming = NN_TRY(utils::makeExecutionFailure(V1_2::utils::convert(timing)));
+    return std::make_pair(std::move(hidlOutputShapes), hidlTiming);
+}
+
+nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> executeSynchronously_1_3(
+        const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request,
+        V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline,
+        const V1_3::OptionalTimeoutDuration& loopTimeoutDuration) {
+    const auto nnRequest = NN_TRY(utils::makeExecutionFailure(convertInput(request)));
+    const auto nnMeasure = NN_TRY(utils::makeExecutionFailure(convertInput(measure)));
+    const auto nnDeadline = NN_TRY(utils::makeExecutionFailure(convertInput(deadline)));
+    const auto nnLoopTimeoutDuration =
+            NN_TRY(utils::makeExecutionFailure(convertInput(loopTimeoutDuration)));
+
+    const auto [outputShapes, timing] =
+            NN_TRY(preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration));
+
+    auto hidlOutputShapes = NN_TRY(utils::makeExecutionFailure(V1_3::utils::convert(outputShapes)));
+    const auto hidlTiming = NN_TRY(utils::makeExecutionFailure(V1_3::utils::convert(timing)));
+    return std::make_pair(std::move(hidlOutputShapes), hidlTiming);
+}
+
+nn::GeneralResult<std::vector<nn::SyncFence>> convertSyncFences(
+        const hidl_vec<hidl_handle>& handles) {
+    std::vector<nn::SyncFence> syncFences;
+    syncFences.reserve(handles.size());
+    for (const auto& handle : handles) {
+        auto nativeHandle = NN_TRY(convertInput(handle));
+        auto syncFence = NN_TRY(utils::makeGeneralFailure(
+                nn::SyncFence::create(std::move(nativeHandle)), nn::ErrorStatus::INVALID_ARGUMENT));
+        syncFences.push_back(std::move(syncFence));
+    }
+    return syncFences;
+}
+
+nn::GeneralResult<std::pair<hidl_handle, sp<V1_3::IFencedExecutionCallback>>> executeFenced(
+        const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request,
+        const hidl_vec<hidl_handle>& waitFor, V1_2::MeasureTiming measure,
+        const V1_3::OptionalTimePoint& deadline,
+        const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+        const V1_3::OptionalTimeoutDuration& duration) {
+    const auto nnRequest = NN_TRY(convertInput(request));
+    const auto nnWaitFor = NN_TRY(convertSyncFences(waitFor));
+    const auto nnMeasure = NN_TRY(convertInput(measure));
+    const auto nnDeadline = NN_TRY(convertInput(deadline));
+    const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration));
+    const auto nnDuration = NN_TRY(convertInput(duration));
+
+    auto [syncFence, executeFencedCallback] = NN_TRY(preparedModel->executeFenced(
+            nnRequest, nnWaitFor, nnMeasure, nnDeadline, nnLoopTimeoutDuration, nnDuration));
+
+    auto hidlSyncFence = NN_TRY(V1_3::utils::convert(syncFence.getSharedHandle()));
+    auto hidlExecuteFencedCallback = sp<FencedExecutionCallback>::make(executeFencedCallback);
+    return std::make_pair(std::move(hidlSyncFence), std::move(hidlExecuteFencedCallback));
+}
+
+}  // namespace
+
+PreparedModel::PreparedModel(nn::SharedPreparedModel preparedModel, Executor executor, uid_t userId)
+    : kPreparedModel(std::move(preparedModel)), kExecutor(std::move(executor)), kUserId(userId) {
+    CHECK(kPreparedModel != nullptr);
+    CHECK(kExecutor != nullptr);
+}
+
+nn::SharedPreparedModel PreparedModel::getUnderlyingPreparedModel() const {
+    return kPreparedModel;
+}
+
+Return<V1_0::ErrorStatus> PreparedModel::execute(const V1_0::Request& request,
+                                                 const sp<V1_0::IExecutionCallback>& callback) {
+    auto result = adapter::execute(kPreparedModel, kUserId, kExecutor, request, callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::PreparedModel::execute failed with " << code << ": " << message;
+        notify(callback.get(), code, {}, {});
+        return V1_0::utils::convert(code).value();
+    }
+    return V1_0::ErrorStatus::NONE;
+}
+
+Return<V1_0::ErrorStatus> PreparedModel::execute_1_2(const V1_0::Request& request,
+                                                     V1_2::MeasureTiming measure,
+                                                     const sp<V1_2::IExecutionCallback>& callback) {
+    auto result =
+            adapter::execute_1_2(kPreparedModel, kUserId, kExecutor, request, measure, callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::PreparedModel::execute_1_2 failed with " << code << ": " << message;
+        notify(callback.get(), code, {}, {});
+        return V1_2::utils::convert(code).value();
+    }
+    return V1_0::ErrorStatus::NONE;
+}
+
+Return<V1_3::ErrorStatus> PreparedModel::execute_1_3(
+        const V1_3::Request& request, V1_2::MeasureTiming measure,
+        const V1_3::OptionalTimePoint& deadline,
+        const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+        const sp<V1_3::IExecutionCallback>& callback) {
+    auto result = adapter::execute_1_3(kPreparedModel, kUserId, kExecutor, request, measure,
+                                       deadline, loopTimeoutDuration, callback);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::PreparedModel::execute_1_3 failed with " << code << ": " << message;
+        notify(callback.get(), code, {}, {});
+        return V1_3::utils::convert(code).value();
+    }
+    return V1_3::ErrorStatus::NONE;
+}
+
+Return<void> PreparedModel::executeSynchronously(const V1_0::Request& request,
+                                                 V1_2::MeasureTiming measure,
+                                                 executeSynchronously_cb cb) {
+    auto result = adapter::executeSynchronously(kPreparedModel, request, measure);
+    if (!result.has_value()) {
+        auto [message, code, outputShapes] = std::move(result).error();
+        LOG(ERROR) << "adapter::PreparedModel::executeSynchronously failed with " << code << ": "
+                   << message;
+        cb(V1_2::utils::convert(code).value(), V1_2::utils::convert(outputShapes).value(),
+           V1_2::utils::kNoTiming);
+        return Void();
+    }
+    auto [outputShapes, timing] = std::move(result).value();
+    cb(V1_0::ErrorStatus::NONE, outputShapes, timing);
+    return Void();
+}
+
+Return<void> PreparedModel::executeSynchronously_1_3(
+        const V1_3::Request& request, V1_2::MeasureTiming measure,
+        const V1_3::OptionalTimePoint& deadline,
+        const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, executeSynchronously_1_3_cb cb) {
+    auto result = adapter::executeSynchronously_1_3(kPreparedModel, request, measure, deadline,
+                                                    loopTimeoutDuration);
+    if (!result.has_value()) {
+        auto [message, code, outputShapes] = std::move(result).error();
+        LOG(ERROR) << "adapter::PreparedModel::executeSynchronously_1_3 failed with " << code
+                   << ": " << message;
+        cb(V1_3::utils::convert(code).value(), V1_3::utils::convert(outputShapes).value(),
+           V1_2::utils::kNoTiming);
+        return Void();
+    }
+    auto [outputShapes, timing] = std::move(result).value();
+    cb(V1_3::ErrorStatus::NONE, outputShapes, timing);
+    return Void();
+}
+
+Return<void> PreparedModel::configureExecutionBurst(
+        const sp<V1_2::IBurstCallback>& callback,
+        const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+        const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
+        configureExecutionBurst_cb cb) {
+    const sp<V1_2::IBurstContext> burst = nn::ExecutionBurstServer::create(
+            callback, requestChannel, resultChannel, this, std::chrono::microseconds{0});
+
+    if (burst == nullptr) {
+        cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+    } else {
+        cb(V1_0::ErrorStatus::NONE, burst);
+    }
+    return Void();
+}
+
+Return<void> PreparedModel::executeFenced(const V1_3::Request& request,
+                                          const hidl_vec<hidl_handle>& waitFor,
+                                          V1_2::MeasureTiming measure,
+                                          const V1_3::OptionalTimePoint& deadline,
+                                          const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
+                                          const V1_3::OptionalTimeoutDuration& duration,
+                                          executeFenced_cb callback) {
+    auto result = adapter::executeFenced(kPreparedModel, request, waitFor, measure, deadline,
+                                         loopTimeoutDuration, duration);
+    if (!result.has_value()) {
+        auto [message, code] = std::move(result).error();
+        LOG(ERROR) << "adapter::PreparedModel::executeFenced failed with " << code << ": "
+                   << message;
+        callback(V1_3::utils::convert(code).value(), {}, nullptr);
+        return Void();
+    }
+    auto [syncFence, executeFencedCallback] = std::move(result).value();
+    callback(V1_3::ErrorStatus::NONE, syncFence, executeFencedCallback);
+    return Void();
+}
+
+}  // namespace android::hardware::neuralnetworks::adapter
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 547f203..2f6112a 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -71,8 +71,8 @@
 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);
+nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
+        size_t numberOfOperands, const std::vector<nn::Operation>& operations);
 
 nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory);
 nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory);
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index 7a5035f..924ecb2 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -246,9 +246,9 @@
     return {};
 }
 
-std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
-                                             const std::vector<nn::Operation>& operations) {
-    return nn::countNumberOfConsumers(numberOfOperands, operations);
+nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
+        size_t numberOfOperands, const std::vector<nn::Operation>& operations) {
+    return makeGeneralFailure(nn::countNumberOfConsumers(numberOfOperands, operations));
 }
 
 nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory) {